You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2024/10/21 19:24:25 UTC

(pulsar-site) branch main updated: Add 4.0.x versioned_docs

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

lhotari pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pulsar-site.git


The following commit(s) were added to refs/heads/main by this push:
     new d6875821bb32 Add 4.0.x versioned_docs
d6875821bb32 is described below

commit d6875821bb325fbaf783ccb8396c711e566be193
Author: Lari Hotari <lh...@users.noreply.github.com>
AuthorDate: Mon Oct 21 22:16:41 2024 +0300

    Add 4.0.x versioned_docs
---
 static/reference/index.html                        |    3 +-
 versioned_docs/version-4.0.x/about.md              |   60 +
 versioned_docs/version-4.0.x/adaptors-kafka.md     |  268 ++
 versioned_docs/version-4.0.x/adaptors-spark.md     |   83 +
 versioned_docs/version-4.0.x/adaptors-storm.md     |   90 +
 versioned_docs/version-4.0.x/admin-api-brokers.md  |  267 ++
 versioned_docs/version-4.0.x/admin-api-clusters.md |  265 ++
 versioned_docs/version-4.0.x/admin-api-features.md |   41 +
 .../version-4.0.x/admin-api-functions.md           |  764 +++++
 .../version-4.0.x/admin-api-namespaces.md          | 1254 ++++++++
 .../admin-api-non-partitioned-topics.md            |    7 +
 .../admin-api-non-persistent-topics.md             |    7 +
 versioned_docs/version-4.0.x/admin-api-overview.md |   31 +
 versioned_docs/version-4.0.x/admin-api-packages.md |  363 +++
 .../version-4.0.x/admin-api-partitioned-topics.md  |    7 +
 .../version-4.0.x/admin-api-permissions.md         |  177 ++
 .../version-4.0.x/admin-api-persistent-topics.md   |    7 +
 versioned_docs/version-4.0.x/admin-api-schemas.md  |  678 +++++
 versioned_docs/version-4.0.x/admin-api-tenants.md  |  234 ++
 versioned_docs/version-4.0.x/admin-api-tools.md    |   49 +
 versioned_docs/version-4.0.x/admin-api-topics.md   | 2064 +++++++++++++
 .../version-4.0.x/admin-api-transactions.md        |  726 +++++
 versioned_docs/version-4.0.x/admin-api-tutorial.md |   85 +
 .../version-4.0.x/admin-api-use-cases.md           |   37 +
 versioned_docs/version-4.0.x/admin-get-started.md  |  529 ++++
 .../administration-anti-affinity-namespaces.md     |   59 +
 versioned_docs/version-4.0.x/administration-geo.md |  286 ++
 .../administration-isolation-bookie.md             |  270 ++
 .../administration-isolation-broker.md             |   61 +
 .../version-4.0.x/administration-isolation.md      |   74 +
 .../version-4.0.x/administration-load-balance.md   |  285 ++
 .../version-4.0.x/administration-metadata-store.md |  111 +
 .../version-4.0.x/administration-proxy.md          |  124 +
 .../version-4.0.x/administration-pulsar-manager.md |  191 ++
 .../version-4.0.x/administration-pulsar-shell.md   |  175 ++
 .../version-4.0.x/administration-stats.md          |  184 ++
 .../version-4.0.x/administration-upgrade.md        |  204 ++
 .../version-4.0.x/administration-zk-bk.md          |  388 +++
 .../version-4.0.x/client-libraries-clients.md      |   87 +
 .../client-libraries-cluster-level-failover.md     |  160 ++
 .../version-4.0.x/client-libraries-consumers.md    |  985 +++++++
 .../client-libraries-cpp-initialize.md             |   18 +
 .../version-4.0.x/client-libraries-cpp-setup.md    |  129 +
 .../version-4.0.x/client-libraries-cpp-use.md      |  184 ++
 .../version-4.0.x/client-libraries-cpp.md          |   73 +
 .../client-libraries-dotnet-initialize.md          |   24 +
 .../version-4.0.x/client-libraries-dotnet-setup.md |   70 +
 .../version-4.0.x/client-libraries-dotnet-use.md   |  209 ++
 .../version-4.0.x/client-libraries-dotnet.md       |   26 +
 .../client-libraries-go-initialize.md              |   59 +
 .../version-4.0.x/client-libraries-go-setup.md     |   65 +
 .../version-4.0.x/client-libraries-go-use.md       |  279 ++
 .../version-4.0.x/client-libraries-go.md           |   30 +
 .../client-libraries-java-initialize.md            |   31 +
 .../version-4.0.x/client-libraries-java-setup.md   |  112 +
 .../version-4.0.x/client-libraries-java-use.md     |  124 +
 .../version-4.0.x/client-libraries-java.md         |   40 +
 .../version-4.0.x/client-libraries-node-configs.md |   75 +
 .../client-libraries-node-initialize.md            |   25 +
 .../version-4.0.x/client-libraries-node-setup.md   |   46 +
 .../version-4.0.x/client-libraries-node-use.md     |  231 ++
 .../version-4.0.x/client-libraries-node.md         |   27 +
 .../version-4.0.x/client-libraries-producers.md    |  672 +++++
 .../client-libraries-python-initialize.md          |   20 +
 .../version-4.0.x/client-libraries-python-setup.md |   58 +
 .../version-4.0.x/client-libraries-python-use.md   |  224 ++
 .../version-4.0.x/client-libraries-python.md       |   30 +
 .../version-4.0.x/client-libraries-readers.md      |  320 +++
 .../version-4.0.x/client-libraries-rest.md         |  126 +
 .../version-4.0.x/client-libraries-schema.md       |  146 +
 .../version-4.0.x/client-libraries-tableviews.md   |  110 +
 .../version-4.0.x/client-libraries-websocket.md    |  618 ++++
 versioned_docs/version-4.0.x/client-libraries.md   |  100 +
 .../concepts-architecture-overview.md              |  169 ++
 .../version-4.0.x/concepts-authentication.md       |    9 +
 .../concepts-broker-load-balancing-benefits.md     |   56 +
 .../concepts-broker-load-balancing-concepts.md     |  642 +++++
 .../concepts-broker-load-balancing-features.md     |   29 +
 .../concepts-broker-load-balancing-migration.md    |  170 ++
 .../concepts-broker-load-balancing-overview.md     |   44 +
 .../concepts-broker-load-balancing-quick-start.md  |  595 ++++
 .../concepts-broker-load-balancing-types.md        |   49 +
 .../concepts-broker-load-balancing-use-cases.md    |   33 +
 versioned_docs/version-4.0.x/concepts-clients.md   |  119 +
 .../concepts-cluster-level-failover.md             |  193 ++
 versioned_docs/version-4.0.x/concepts-messaging.md | 1234 ++++++++
 .../version-4.0.x/concepts-multi-tenancy.md        |   58 +
 .../concepts-multiple-advertised-listeners.md      |   39 +
 versioned_docs/version-4.0.x/concepts-overview.md  |   36 +
 .../version-4.0.x/concepts-proxy-sni-routing.md    |  165 ++
 .../version-4.0.x/concepts-replication.md          |   65 +
 .../version-4.0.x/concepts-throttling.md           |  168 ++
 .../version-4.0.x/concepts-tiered-storage.md       |   17 +
 .../version-4.0.x/concepts-topic-compaction.md     |   54 +
 .../version-4.0.x/concepts-transactions.md         |   29 +
 .../version-4.0.x/cookbooks-bookkeepermetadata.md  |   21 +
 .../version-4.0.x/cookbooks-compaction.md          |  127 +
 .../version-4.0.x/cookbooks-deduplication.md       |  145 +
 .../version-4.0.x/cookbooks-encryption.md          |   12 +
 .../version-4.0.x/cookbooks-message-queue.md       |  140 +
 .../version-4.0.x/cookbooks-non-persistent.md      |   57 +
 .../version-4.0.x/cookbooks-partitioned.md         |    6 +
 .../version-4.0.x/cookbooks-retention-expiry.md    |  482 ++++
 .../version-4.0.x/cookbooks-tiered-storage.md      |  308 ++
 versioned_docs/version-4.0.x/deploy-aws.md         |  231 ++
 .../deploy-bare-metal-multi-cluster.md             |  396 +++
 versioned_docs/version-4.0.x/deploy-bare-metal.md  |  496 ++++
 versioned_docs/version-4.0.x/deploy-docker.md      |   90 +
 versioned_docs/version-4.0.x/deploy-ibm.md         |  241 ++
 versioned_docs/version-4.0.x/deploy-kubernetes.md  |   27 +
 versioned_docs/version-4.0.x/deploy-monitoring.md  |  239 ++
 .../version-4.0.x/develop-load-manager.md          |  435 +++
 versioned_docs/version-4.0.x/develop-plugin.md     |  239 ++
 versioned_docs/version-4.0.x/develop-tools.md      |  105 +
 versioned_docs/version-4.0.x/developers-landing.md |   25 +
 .../version-4.0.x/developing-binary-protocol.md    |  636 +++++
 versioned_docs/version-4.0.x/functions-cli.md      |  193 ++
 versioned_docs/version-4.0.x/functions-concepts.md |  194 ++
 .../version-4.0.x/functions-debug-cli.md           |  183 ++
 .../version-4.0.x/functions-debug-localrun.md      |   70 +
 .../version-4.0.x/functions-debug-log-topic.md     |   47 +
 .../version-4.0.x/functions-debug-stderr.md        |    8 +
 .../version-4.0.x/functions-debug-unit-test.md     |   64 +
 versioned_docs/version-4.0.x/functions-debug.md    |   12 +
 .../version-4.0.x/functions-deploy-arguments.md    |   33 +
 .../functions-deploy-cluster-builtin.md            |   29 +
 .../functions-deploy-cluster-encryption.md         |   27 +
 .../functions-deploy-cluster-package.md            |   24 +
 .../functions-deploy-cluster-parallelism.md        |   46 +
 .../functions-deploy-cluster-resource.md           |   33 +
 .../version-4.0.x/functions-deploy-cluster.md      |   36 +
 .../version-4.0.x/functions-deploy-localrun.md     |   34 +
 .../version-4.0.x/functions-deploy-trigger.md      |   67 +
 versioned_docs/version-4.0.x/functions-deploy.md   |   21 +
 .../version-4.0.x/functions-develop-admin-api.md   |   50 +
 .../version-4.0.x/functions-develop-api.md         |  231 ++
 .../version-4.0.x/functions-develop-log.md         |  175 ++
 .../version-4.0.x/functions-develop-metrics.md     |   90 +
 .../functions-develop-schema-registry.md           |   23 +
 .../version-4.0.x/functions-develop-security.md    |   95 +
 .../version-4.0.x/functions-develop-serde.md       |  147 +
 .../version-4.0.x/functions-develop-state.md       |  314 ++
 .../version-4.0.x/functions-develop-tutorial.md    |  116 +
 .../functions-develop-user-defined-configs.md      |  147 +
 versioned_docs/version-4.0.x/functions-develop.md  |   24 +
 versioned_docs/version-4.0.x/functions-overview.md |   80 +
 .../version-4.0.x/functions-package-go.md          |   41 +
 .../version-4.0.x/functions-package-java.md        |  197 ++
 .../version-4.0.x/functions-package-python.md      |  179 ++
 versioned_docs/version-4.0.x/functions-package.md  |   19 +
 .../version-4.0.x/functions-quickstart.md          |  627 ++++
 .../functions-runtime-java-options.md              |   21 +
 .../version-4.0.x/functions-runtime-kubernetes.md  |  199 ++
 .../version-4.0.x/functions-runtime-process.md     |   59 +
 .../version-4.0.x/functions-runtime-thread.md      |   35 +
 versioned_docs/version-4.0.x/functions-runtime.md  |   21 +
 .../version-4.0.x/functions-worker-corun.md        |   58 +
 .../functions-worker-for-geo-replication.md        |   20 +
 .../functions-worker-run-separately.md             |  227 ++
 .../version-4.0.x/functions-worker-stateful.md     |   71 +
 .../functions-worker-temp-file-path.md             |   15 +
 .../functions-worker-troubleshooting.md            |   42 +
 versioned_docs/version-4.0.x/functions-worker.md   |   19 +
 .../version-4.0.x/get-started-pulsar-admin.md      |  219 ++
 .../getting-started-docker-compose.md              |  136 +
 .../version-4.0.x/getting-started-docker.md        |  296 ++
 .../version-4.0.x/getting-started-helm.md          |  400 +++
 .../version-4.0.x/getting-started-home.md          |   12 +
 .../version-4.0.x/getting-started-standalone.md    |  142 +
 versioned_docs/version-4.0.x/helm-deploy.md        |  418 +++
 versioned_docs/version-4.0.x/helm-install.md       |   12 +
 versioned_docs/version-4.0.x/helm-overview.md      |   12 +
 versioned_docs/version-4.0.x/helm-prepare.md       |   28 +
 versioned_docs/version-4.0.x/helm-tools.md         |   12 +
 versioned_docs/version-4.0.x/helm-upgrade.md       |   37 +
 versioned_docs/version-4.0.x/how-to-landing.md     |   13 +
 .../install-deploy-upgrade-landing.md              |   24 +
 versioned_docs/version-4.0.x/io-aerospike-sink.md  |   31 +
 versioned_docs/version-4.0.x/io-aerospike.md       |    5 +
 versioned_docs/version-4.0.x/io-alluxio.md         |   68 +
 .../version-4.0.x/io-azuredataexplorer-sink.md     |   77 +
 versioned_docs/version-4.0.x/io-canal-source.md    |  208 ++
 versioned_docs/version-4.0.x/io-cassandra-sink.md  |   60 +
 versioned_docs/version-4.0.x/io-cassandra.md       |    5 +
 versioned_docs/version-4.0.x/io-cdc-canal.md       |    5 +
 versioned_docs/version-4.0.x/io-cdc-debezium.md    |  516 ++++
 versioned_docs/version-4.0.x/io-cdc.md             |   26 +
 versioned_docs/version-4.0.x/io-cli.md             |  586 ++++
 versioned_docs/version-4.0.x/io-connectors.md      |  283 ++
 versioned_docs/version-4.0.x/io-debezium-source.md |  748 +++++
 versioned_docs/version-4.0.x/io-debug.md           |  392 +++
 versioned_docs/version-4.0.x/io-develop.md         |  395 +++
 versioned_docs/version-4.0.x/io-dynamodb-source.md |   83 +
 versioned_docs/version-4.0.x/io-dynamodb.md        |    5 +
 .../version-4.0.x/io-elasticsearch-sink.md         |  238 ++
 versioned_docs/version-4.0.x/io-elasticsearch.md   |    5 +
 versioned_docs/version-4.0.x/io-file-source.md     |  154 +
 versioned_docs/version-4.0.x/io-file.md            |    5 +
 versioned_docs/version-4.0.x/io-flume-sink.md      |   59 +
 versioned_docs/version-4.0.x/io-flume-source.md    |   59 +
 versioned_docs/version-4.0.x/io-hbase-sink.md      |   68 +
 versioned_docs/version-4.0.x/io-hbase.md           |    5 +
 versioned_docs/version-4.0.x/io-hdfs.md            |    5 +
 versioned_docs/version-4.0.x/io-hdfs2-sink.md      |   66 +
 versioned_docs/version-4.0.x/io-hdfs3-sink.md      |   61 +
 versioned_docs/version-4.0.x/io-http-sink.md       |   62 +
 versioned_docs/version-4.0.x/io-influxdb-sink.md   |  118 +
 versioned_docs/version-4.0.x/io-influxdb.md        |    5 +
 versioned_docs/version-4.0.x/io-jdbc-sink.md       |  187 ++
 versioned_docs/version-4.0.x/io-jdbc.md            |    5 +
 versioned_docs/version-4.0.x/io-kafka-sink.md      |   82 +
 versioned_docs/version-4.0.x/io-kafka-source.md    |  222 ++
 versioned_docs/version-4.0.x/io-kafka.md           |    5 +
 versioned_docs/version-4.0.x/io-kinesis-sink.md    |   83 +
 versioned_docs/version-4.0.x/io-kinesis-source.md  |   87 +
 versioned_docs/version-4.0.x/io-kinesis.md         |    5 +
 versioned_docs/version-4.0.x/io-mongo-sink.md      |   58 +
 versioned_docs/version-4.0.x/io-mongo-source.md    |   61 +
 versioned_docs/version-4.0.x/io-mongo.md           |    5 +
 versioned_docs/version-4.0.x/io-netty-source.md    |  204 ++
 versioned_docs/version-4.0.x/io-netty.md           |    5 +
 versioned_docs/version-4.0.x/io-nsq-source.md      |   26 +
 versioned_docs/version-4.0.x/io-nsq.md             |    5 +
 versioned_docs/version-4.0.x/io-overview.md        |  163 ++
 versioned_docs/version-4.0.x/io-quickstart.md      |  849 ++++++
 versioned_docs/version-4.0.x/io-rabbitmq-sink.md   |   84 +
 versioned_docs/version-4.0.x/io-rabbitmq-source.md |  188 ++
 versioned_docs/version-4.0.x/io-rabbitmq.md        |    5 +
 versioned_docs/version-4.0.x/io-redis-sink.md      |  142 +
 versioned_docs/version-4.0.x/io-redis.md           |    5 +
 versioned_docs/version-4.0.x/io-solr-sink.md       |   64 +
 versioned_docs/version-4.0.x/io-solr.md            |    5 +
 versioned_docs/version-4.0.x/io-tcp.md             |    5 +
 versioned_docs/version-4.0.x/io-twitter-source.md  |   33 +
 versioned_docs/version-4.0.x/io-twitter.md         |    6 +
 versioned_docs/version-4.0.x/io-use.md             | 1673 +++++++++++
 versioned_docs/version-4.0.x/kubernetes-helm.md    |  369 +++
 .../version-4.0.x/performance-pulsar-perf.md       |  247 ++
 .../version-4.0.x/pulsar-api-overview.md           |   50 +
 .../version-4.0.x/reference-cli-tools.md           |   30 +
 .../version-4.0.x/reference-configuration.md       |    7 +
 .../version-4.0.x/reference-connector-admin.md     |   12 +
 versioned_docs/version-4.0.x/reference-landing.md  |   15 +
 .../reference-metrics-opentelemetry.md             |  734 +++++
 versioned_docs/version-4.0.x/reference-metrics.md  |  851 ++++++
 .../version-4.0.x/reference-pulsar-admin.md        | 3021 ++++++++++++++++++++
 .../version-4.0.x/reference-rest-api-overview.md   |   21 +
 .../version-4.0.x/reference-terminology.md         |   95 +
 .../schema-evolution-compatibility.md              |   12 +
 versioned_docs/version-4.0.x/schema-get-started.md |  957 +++++++
 versioned_docs/version-4.0.x/schema-manage.md      |   12 +
 versioned_docs/version-4.0.x/schema-overview.md    |  148 +
 versioned_docs/version-4.0.x/schema-understand.md  |  378 +++
 versioned_docs/version-4.0.x/security-athenz.md    |  308 ++
 .../version-4.0.x/security-authorization.md        |  118 +
 .../version-4.0.x/security-basic-auth.md           |  154 +
 .../version-4.0.x/security-bouncy-castle.md        |  143 +
 .../version-4.0.x/security-encryption.md           |  389 +++
 versioned_docs/version-4.0.x/security-extending.md |   91 +
 versioned_docs/version-4.0.x/security-jwt.md       |  244 ++
 versioned_docs/version-4.0.x/security-kerberos.md  |  319 +++
 versioned_docs/version-4.0.x/security-oauth2.md    |  268 ++
 .../version-4.0.x/security-openid-connect.md       |  147 +
 versioned_docs/version-4.0.x/security-overview.md  |   82 +
 .../version-4.0.x/security-tls-authentication.md   |  296 ++
 .../version-4.0.x/security-tls-transport.md        |  683 +++++
 .../version-4.0.x/security-token-admin.md          |  162 ++
 versioned_docs/version-4.0.x/standalone-docker.md  |  199 ++
 versioned_docs/version-4.0.x/standalone.md         |   12 +
 .../version-4.0.x/tiered-storage-aliyun.md         |  205 ++
 versioned_docs/version-4.0.x/tiered-storage-aws.md |  249 ++
 .../version-4.0.x/tiered-storage-azure.md          |  192 ++
 .../version-4.0.x/tiered-storage-filesystem.md     |  575 ++++
 versioned_docs/version-4.0.x/tiered-storage-gcs.md |  243 ++
 .../version-4.0.x/tiered-storage-overview.md       |   88 +
 versioned_docs/version-4.0.x/tiered-storage-s3.md  |  201 ++
 versioned_docs/version-4.0.x/transaction-api.md    |  149 +
 .../version-4.0.x/transaction-guarantee.md         |   16 +
 .../version-4.0.x/tutorials-namespace.md           |   39 +
 .../version-4.0.x/tutorials-produce-consume.md     |   79 +
 versioned_docs/version-4.0.x/tutorials-tenant.md   |   56 +
 versioned_docs/version-4.0.x/tutorials-topic.md    |   43 +
 .../version-4.0.x/txn-advanced-features.md         |   46 +
 versioned_docs/version-4.0.x/txn-how.md            |  143 +
 versioned_docs/version-4.0.x/txn-monitor.md        |   10 +
 versioned_docs/version-4.0.x/txn-use.md            |  306 ++
 versioned_docs/version-4.0.x/txn-what.md           |   60 +
 versioned_docs/version-4.0.x/txn-why.md            |   45 +
 .../version-4.0.x/window-functions-context.md      |  530 ++++
 versioned_sidebars/version-4.0.x-sidebars.json     |  535 ++++
 versions.json                                      |    1 +
 291 files changed, 53745 insertions(+), 1 deletion(-)

diff --git a/static/reference/index.html b/static/reference/index.html
index 4ca10802a8cb..470f2c2884a5 100644
--- a/static/reference/index.html
+++ b/static/reference/index.html
@@ -205,6 +205,7 @@
           '<option value="3.1.x">3.1.x</value>' +
           '<option value="3.2.x">3.2.x</value>' +
           '<option value="3.3.x">3.3.x</value>' +
+          '<option value="4.0.x">4.0.x</value>' +
           '<option value="next">next</value>' +
           "</select>",
         repo: "apache/pulsar",
@@ -217,7 +218,7 @@
         namespaces: [
           {
             id: "version",
-            values: ["2.6.x", "2.7.x", "2.8.x", "2.9.x", "2.10.x", "2.11.x", "3.0.x", "3.1.x", "3.2.x", "3.3.x", "next"],
+            values: ["2.6.x", "2.7.x", "2.8.x", "2.9.x", "2.10.x", "2.11.x", "3.0.x", "3.1.x", "3.2.x", "3.3.x", "4.0.x", "next"],
             optional: true,
             selector: "#ver-selector",
             default: "next",
diff --git a/versioned_docs/version-4.0.x/about.md b/versioned_docs/version-4.0.x/about.md
new file mode 100644
index 000000000000..72ad7aea3aa5
--- /dev/null
+++ b/versioned_docs/version-4.0.x/about.md
@@ -0,0 +1,60 @@
+---
+slug: /
+id: about
+title: Welcome to the Apache Pulsar documentation portal
+sidebar_label: "About"
+description: Explore the Apache Pulsar documentation and join the knowledgeable Pulsar community.
+---
+
+import BlockLinks from "@site/src/components/ui/BlockLinks";
+import BlockLink from "@site/src/components/ui/BlockLink";
+import { docUrl } from "@site/src/utils/index";
+
+# Welcome to the Apache Pulsarโ„ข documentation portal
+***
+
+This portal holds a variety of topics, tutorials, guides, and reference material to help you work with Pulsar.
+
+## Choose your path
+Select one of the content blocks below to begin your Pulsar journey. If you ...
+* Are new to Pulsar, start with **About Pulsar** to learn about features and concepts.
+* Want to jump to the quickstart, select **Get Started**.
+* Are an operator responsible for architecting and supporting Apache Pulsar, start with **Install, Deploy, Upgrade**.
+* Are a developer who wants to master Apache Pulsar, select **Pulsar for Developers**.
+* Want to try out Pulsar, select **How To** for access to the "hello world" tutorial.
+* An experienced Pulsar coder looking for API, metrics, or configuration documentation, go to **Reference**.
+
+<BlockLinks>
+    <BlockLink title="About Pulsar" url="concepts-overview" />
+    <BlockLink title="Get Started" url="getting-started-home" />
+    <BlockLink title="Install, Deploy, Upgrade" url="install-deploy-upgrade-landing" />
+    <BlockLink title="Pulsar for Developers" url="developers-landing" />
+    <BlockLink title="How To" url="how-to-landing" />
+    <BlockLink title="Reference" url="reference-landing" />
+</BlockLinks>
+
+## Continuous Improvement
+***
+
+As you probably know, we are working on a new user experience for our documentation portal that will make learning about and building on top of Apache Pulsar a much better experience. Whether you need overview concepts, how-to procedures, curated guides or quick references, we're building content to support it. This welcome page is just the first step. We will be providing updates every month.
+
+## Help Improve These Documents
+***
+
+Participation is not only welcomed โ€“ it's essential!
+
+๐Ÿ“š There are multiple ways you can help to make the Pulsar documentation better:
+
+- **Submit a Pull Request**: You'll find the "Edit this page" link at the bottom of each page. Click the link and follow the [Pulsar Documentation Contribution Guide](pathname:///contribute/document-intro).
+
+- **๐Ÿ’ก Suggest changes**: If you found a mistake, the documentation is incomplete, but for any reason you're not ready to submit a PR, or you have any other suggestion, please click the appropriate link at the top right of any documentation page.
+- **๐Ÿ›Ÿ Get support**: If something isn't clear for you in the documentation, or you have general questions about Pulsar, you can ask the community by clicking the appropriate link at the top right of any documentation page. This way you will help people searching for the answer to this question in the future after someone from the community answers your question.
+
+## Join the Community!
+***
+
+The Pulsar community on GitHub is active, passionate, and knowledgeable.  Join discussions, voice opinions, suggest features, and dive into the code itself. Find your Pulsar family here at [apache/pulsar](https://github.com/apache/pulsar).
+
+* Please go to [the community page](pathname:///community/#section-discussions) to find the contact information for joining various communication channels.
+* The main communication channel for the Apache Pulsar project are [the mailing lists](pathname:///contact/).
+* There's a separate page for Apache Pulsar [Security advisories and Security policy](pathname:///security/).
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/adaptors-kafka.md b/versioned_docs/version-4.0.x/adaptors-kafka.md
new file mode 100644
index 000000000000..4718b1bd6482
--- /dev/null
+++ b/versioned_docs/version-4.0.x/adaptors-kafka.md
@@ -0,0 +1,268 @@
+---
+id: adaptors-kafka
+title: Pulsar adaptor for Apache Kafka
+sidebar_label: "Kafka client wrapper"
+description: Learn to use the Pulsar Kafka compatibility wrapper and configure Pulsar authentication provider.
+---
+
+
+Pulsar provides an easy option for applications that are currently written using the [Apache Kafka](http://kafka.apache.org) Java client API.
+
+## Use the Pulsar Kafka compatibility wrapper
+
+To use the Pulsar Kafka compatibility wrapper, complete the following steps.
+
+**Step 1:** In an existing application, change the regular Kafka client dependency and replace it with the Pulsar Kafka wrapper. Remove the following dependency in `pom.xml`:
+
+```xml
+<dependency>
+  <groupId>org.apache.kafka</groupId>
+  <artifactId>kafka-clients</artifactId>
+  <version>0.10.2.1</version>
+</dependency>
+```
+
+**Step 2:** Then include this dependency for the Pulsar Kafka wrapper:
+
+```xml
+<dependency>
+  <groupId>org.apache.pulsar</groupId>
+  <artifactId>pulsar-client-kafka</artifactId>
+  <version>@pulsar:version@</version>
+</dependency>
+```
+
+With the new dependency, the existing code works without any changes. You need to adjust the configuration, and make sure it points the
+producers and consumers to Pulsar service rather than Kafka, and uses a particular
+Pulsar topic.
+
+## Use the Pulsar Kafka compatibility wrapper together with existing Kafka client
+
+When migrating from Kafka to Pulsar, the application might use the original Kafka client
+and the Pulsar Kafka wrapper together during migration. You should consider using the
+unshaded Pulsar Kafka client wrapper.
+
+```xml
+<dependency>
+  <groupId>org.apache.pulsar</groupId>
+  <artifactId>pulsar-client-kafka-original</artifactId>
+  <version>@pulsar:version@</version>
+</dependency>
+```
+
+When using this dependency, construct producers using `org.apache.kafka.clients.producer.PulsarKafkaProducer`
+instead of `org.apache.kafka.clients.producer.KafkaProducer` and `org.apache.kafka.clients.producer.PulsarKafkaConsumer` for consumers.
+
+## Producer example
+
+```java
+// Topic needs to be a regular Pulsar topic
+String topic = "persistent://public/default/my-topic";
+
+Properties props = new Properties();
+// Point to a Pulsar service
+props.put("bootstrap.servers", "pulsar://localhost:6650");
+
+props.put("key.serializer", IntegerSerializer.class.getName());
+props.put("value.serializer", StringSerializer.class.getName());
+
+Producer<Integer, String> producer = new KafkaProducer(props);
+
+for (int i = 0; i < 10; i++) {
+    producer.send(new ProducerRecord<Integer, String>(topic, i, "hello-" + i));
+    log.info("Message {} sent successfully", i);
+}
+
+producer.close();
+```
+
+## Consumer example
+
+```java
+String topic = "persistent://public/default/my-topic";
+
+Properties props = new Properties();
+// Point to a Pulsar service
+props.put("bootstrap.servers", "pulsar://localhost:6650");
+props.put("group.id", "my-subscription-name");
+props.put("enable.auto.commit", "false");
+props.put("key.deserializer", IntegerDeserializer.class.getName());
+props.put("value.deserializer", StringDeserializer.class.getName());
+
+Consumer<Integer, String> consumer = new KafkaConsumer(props);
+consumer.subscribe(Arrays.asList(topic));
+
+while (true) {
+    ConsumerRecords<Integer, String> records = consumer.poll(100);
+    records.forEach(record -> {
+        log.info("Received record: {}", record);
+    });
+
+    // Commit last offset
+    consumer.commitSync();
+}
+```
+
+## Complete Examples
+
+You can find the complete producer and consumer examples [here](https://github.com/apache/pulsar-adapters/tree/master/pulsar-client-kafka-compat/pulsar-client-kafka-tests/src/test/java/org/apache/pulsar/client/kafka/compat/examples).
+
+## Compatibility matrix
+
+Currently, the Pulsar Kafka wrapper supports most of the operations offered by the Kafka API.
+
+### Producer
+
+APIs:
+
+| Producer Method                                                               | Supported | Notes                                                                    |
+|:------------------------------------------------------------------------------|:----------|:-------------------------------------------------------------------------|
+| `Future<RecordMetadata> send(ProducerRecord<K, V> record)`                    | Yes       |                                                                          |
+| `Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback)` | Yes       |                                                                          |
+| `void flush()`                                                                | Yes       |                                                                          |
+| `List<PartitionInfo> partitionsFor(String topic)`                             | No        |                                                                          |
+| `Map<MetricName, ? extends Metric> metrics()`                                 | No        |                                                                          |
+| `void close()`                                                                | Yes       |                                                                          |
+| `void close(long timeout, TimeUnit unit)`                                     | Yes       |                                                                          |
+
+Properties:
+
+| Config property                         | Supported | Notes                                                                         |
+|:----------------------------------------|:----------|:------------------------------------------------------------------------------|
+| `acks`                                  | Ignored   | Durability and quorum writes are configured at the namespace level            |
+| `auto.offset.reset`                     | Yes       | It uses a default value of `earliest` if you do not give a specific setting. |
+| `batch.size`                            | Ignored   |                                                                               |
+| `bootstrap.servers`                     | Yes       |                                 |
+| `buffer.memory`                         | Ignored   |                                                                               |
+| `client.id`                             | Ignored   |                                                                               |
+| `compression.type`                      | Yes       | Allows `gzip` and `lz4`. No `snappy`.                                         |
+| `connections.max.idle.ms`               | Yes       | Only support up to 2,147,483,647,000(Integer.MAX_VALUE * 1000) ms of idle time|
+| `interceptor.classes`                   | Yes       |                                                                               |
+| `key.serializer`                        | Yes       |                                                                               |
+| `linger.ms`                             | Yes       | Controls the group commit time when batching messages                         |
+| `max.block.ms`                          | Ignored   |                                                                               |
+| `max.in.flight.requests.per.connection` | Ignored   | In Pulsar ordering is maintained even with multiple requests in flight        |
+| `max.request.size`                      | Ignored   |                                                                               |
+| `metric.reporters`                      | Ignored   |                                                                               |
+| `metrics.num.samples`                   | Ignored   |                                                                               |
+| `metrics.sample.window.ms`              | Ignored   |                                                                               |
+| `partitioner.class`                     | Yes       |                                                                               |
+| `receive.buffer.bytes`                  | Ignored   |                                                                               |
+| `reconnect.backoff.ms`                  | Ignored   |                                                                               |
+| `request.timeout.ms`                    | Ignored   |                                                                               |
+| `retries`                               | Ignored   | Pulsar client retries with exponential backoff until the send timeout expires. |
+| `send.buffer.bytes`                     | Ignored   |                                                                               |
+| `timeout.ms`                            | Yes       |                                                                               |
+| `value.serializer`                      | Yes       |                                                                               |
+
+
+### Consumer
+
+The following table lists consumer APIs.
+
+| Consumer Method                                                                                         | Supported | Notes |
+|:--------------------------------------------------------------------------------------------------------|:----------|:------|
+| `Set<TopicPartition> assignment()`                                                                      | No        |       |
+| `Set<String> subscription()`                                                                            | Yes       |       |
+| `void subscribe(Collection<String> topics)`                                                             | Yes       |       |
+| `void subscribe(Collection<String> topics, ConsumerRebalanceListener callback)`                         | No        |       |
+| `void assign(Collection<TopicPartition> partitions)`                                                    | No        |       |
+| `void subscribe(Pattern pattern, ConsumerRebalanceListener callback)`                                   | No        |       |
+| `void unsubscribe()`                                                                                    | Yes       |       |
+| `ConsumerRecords<K, V> poll(long timeoutMillis)`                                                        | Yes       |       |
+| `void commitSync()`                                                                                     | Yes       |       |
+| `void commitSync(Map<TopicPartition, OffsetAndMetadata> offsets)`                                       | Yes       |       |
+| `void commitAsync()`                                                                                    | Yes       |       |
+| `void commitAsync(OffsetCommitCallback callback)`                                                       | Yes       |       |
+| `void commitAsync(Map<TopicPartition, OffsetAndMetadata> offsets, OffsetCommitCallback callback)`       | Yes       |       |
+| `void seek(TopicPartition partition, long offset)`                                                      | Yes       |       |
+| `void seekToBeginning(Collection<TopicPartition> partitions)`                                           | Yes       |       |
+| `void seekToEnd(Collection<TopicPartition> partitions)`                                                 | Yes       |       |
+| `long position(TopicPartition partition)`                                                               | Yes       |       |
+| `OffsetAndMetadata committed(TopicPartition partition)`                                                 | Yes       |       |
+| `Map<MetricName, ? extends Metric> metrics()`                                                           | No        |       |
+| `List<PartitionInfo> partitionsFor(String topic)`                                                       | No        |       |
+| `Map<String, List<PartitionInfo>> listTopics()`                                                         | No        |       |
+| `Set<TopicPartition> paused()`                                                                          | No        |       |
+| `void pause(Collection<TopicPartition> partitions)`                                                     | No        |       |
+| `void resume(Collection<TopicPartition> partitions)`                                                    | No        |       |
+| `Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes(Map<TopicPartition, Long> timestampsToSearch)` | No        |       |
+| `Map<TopicPartition, Long> beginningOffsets(Collection<TopicPartition> partitions)`                     | No        |       |
+| `Map<TopicPartition, Long> endOffsets(Collection<TopicPartition> partitions)`                           | No        |       |
+| `void close()`                                                                                          | Yes       |       |
+| `void close(long timeout, TimeUnit unit)`                                                               | Yes       |       |
+| `void wakeup()`                                                                                         | No        |       |
+
+Properties:
+
+| Config property                 | Supported | Notes                                                 |
+|:--------------------------------|:----------|:------------------------------------------------------|
+| `group.id`                      | Yes       | Maps to a Pulsar subscription name                    |
+| `max.poll.records`              | Yes       |                                                       |
+| `max.poll.interval.ms`          | Ignored   | Messages are "pushed" from broker                     |
+| `session.timeout.ms`            | Ignored   |                                                       |
+| `heartbeat.interval.ms`         | Ignored   |                                                       |
+| `bootstrap.servers`             | Yes       | Needs to point to a single Pulsar service URL         |
+| `enable.auto.commit`            | Yes       |                                                       |
+| `auto.commit.interval.ms`       | Ignored   | With auto-commit, acks are sent immediately to broker |
+| `partition.assignment.strategy` | Ignored   |                                                       |
+| `auto.offset.reset`             | Yes       | Only support earliest and latest.                     |
+| `fetch.min.bytes`               | Ignored   |                                                       |
+| `fetch.max.bytes`               | Ignored   |                                                       |
+| `fetch.max.wait.ms`             | Ignored   |                                                       |
+| `interceptor.classes`           | Yes       |                                                       |
+| `metadata.max.age.ms`           | Ignored   |                                                       |
+| `max.partition.fetch.bytes`     | Ignored   |                                                       |
+| `send.buffer.bytes`             | Ignored   |                                                       |
+| `receive.buffer.bytes`          | Ignored   |                                                       |
+| `client.id`                     | Ignored   |                                                       |
+
+
+## Customize Pulsar configurations
+
+You can configure Pulsar authentication provider directly from the Kafka properties.
+
+### Pulsar client properties
+
+| Config property                        | Default | Notes                                                                                  |
+|:---------------------------------------|:--------|:---------------------------------------------------------------------------------------|
+| [`pulsar.authentication.class`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setAuthentication-org.apache.pulsar.client.api.Authentication-)          |         | Configure to auth provider. For example, `org.apache.pulsar.client.impl.auth.AuthenticationTls`.|
+| [`pulsar.authentication.params.map`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setAuthentication-java.lang.String-java.util.Map-)          |         | Map which represents parameters for the Authentication-Plugin. |
+| [`pulsar.authentication.params.string`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setAuthentication-java.lang.String-java.lang.String-)          |         | String which represents parameters for the Authentication-Plugin, for example, `key1:val1,key2:val2`. |
+| [`pulsar.use.tls`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setUseTls-boolean-)                       | `false` | Enable TLS transport encryption.                                                        |
+| [`pulsar.tls.trust.certs.file.path`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setTlsTrustCertsFilePath-java.lang.String-)   |         | Path for the TLS trust certificate store.                                               |
+| [`pulsar.tls.allow.insecure.connection`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setTlsAllowInsecureConnection-boolean-) | `false` | Accept self-signed certificates from brokers.                                           |
+| [`pulsar.operation.timeout.ms`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setOperationTimeout-int-java.util.concurrent.TimeUnit-) | `30000` | General operations timeout. |
+| [`pulsar.stats.interval.seconds`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setStatsInterval-long-java.util.concurrent.TimeUnit-) | `60` | Pulsar client lib stats printing interval. |
+| [`pulsar.num.io.threads`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setIoThreads-int-) | `1` | The number of Netty IO threads to use. |
+| [`pulsar.connections.per.broker`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setConnectionsPerBroker-int-) | `1` | The maximum number of connection to each broker. |
+| [`pulsar.use.tcp.nodelay`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setUseTcpNoDelay-boolean-) | `true` | TCP no-delay. |
+| [`pulsar.concurrent.lookup.requests`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setConcurrentLookupRequest-int-) | `50000` | The maximum number of concurrent topic lookups. |
+| [`pulsar.max.number.rejected.request.per.connection`](/api/client/org/apache/pulsar/client/api/ClientConfiguration.html#setMaxNumberOfRejectedRequestPerConnection-int-) | `50` | The threshold of errors to forcefully close a connection. |
+| [`pulsar.keepalive.interval.ms`](/api/client/org/apache/pulsar/client/api/ClientBuilder.html#keepAliveInterval-int-java.util.concurrent.TimeUnit-)| `30000` | Keep alive interval for each client-broker-connection.  |
+
+
+### Pulsar producer properties
+
+| Config property                        | Default | Notes                                                                                  |
+|:---------------------------------------|:--------|:---------------------------------------------------------------------------------------|
+| [`pulsar.producer.name`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setProducerName-java.lang.String-) | | Specify the producer name. |
+| [`pulsar.producer.initial.sequence.id`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setInitialSequenceId-long-) |  | Specify baseline for sequence ID of this producer. |
+| [`pulsar.producer.max.pending.messages`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setMaxPendingMessages-int-) | `1000` | Set the maximum size of the message queue pending to receive an acknowledgment from the broker.  |
+| [`pulsar.producer.max.pending.messages.across.partitions`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setMaxPendingMessagesAcrossPartitions-int-) | `50000` | Set the maximum number of pending messages across all the partitions.  |
+| [`pulsar.producer.batching.enabled`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setBatchingEnabled-boolean-) | `true` | Control whether automatic batching of messages is enabled for the producer. |
+| [`pulsar.producer.batching.max.messages`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setBatchingMaxMessages-int-) | `1000` | The maximum number of messages in a batch. |
+| [`pulsar.block.if.producer.queue.full`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setBlockIfQueueFull-boolean-) | | Specify the block producer if queue  is full. |
+| [`pulsar.crypto.reader.factory.class.name`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setCryptoKeyReader-org.apache.pulsar.client.api.CryptoKeyReader-) | | Specify the CryptoReader-Factory(`CryptoKeyReaderFactory`) classname which allows producer to create CryptoKeyReader. |
+
+
+### Pulsar consumer Properties
+
+| Config property                        | Default | Notes                                                                                  |
+|:---------------------------------------|:--------|:---------------------------------------------------------------------------------------|
+| [`pulsar.consumer.name`](/api/client/org/apache/pulsar/client/api/ConsumerConfiguration.html#setConsumerName-java.lang.String-) | | Specify the consumer name. |
+| [`pulsar.consumer.receiver.queue.size`](/api/client/org/apache/pulsar/client/api/ConsumerConfiguration.html#setReceiverQueueSize-int-) | 1000 | Set the size of the consumer receiver queue. |
+| [`pulsar.consumer.acknowledgments.group.time.millis`](/api/client/org/apache/pulsar/client/api/ConsumerBuilder.html#acknowledgmentGroupTime-long-java.util.concurrent.TimeUnit-) | 100 | Set the maximum amount of group time for consumers to send the acknowledgments to the broker. |
+| [`pulsar.consumer.total.receiver.queue.size.across.partitions`](/api/client/org/apache/pulsar/client/api/ConsumerConfiguration.html#setMaxTotalReceiverQueueSizeAcrossPartitions-int-) | 50000 | Set the maximum size of the total receiver queue across partitions. |
+| [`pulsar.consumer.subscription.topics.mode`](/api/client/org/apache/pulsar/client/api/ConsumerBuilder.html#subscriptionTopicsMode-Mode-) | PersistentOnly | Set the subscription topic mode for consumers. |
+| [`pulsar.crypto.reader.factory.class.name`](/api/client/org/apache/pulsar/client/api/ProducerConfiguration.html#setCryptoKeyReader-org.apache.pulsar.client.api.CryptoKeyReader-) | | Specify the CryptoReader-Factory(`CryptoKeyReaderFactory`) classname which allows consumer to create CryptoKeyReader. |
diff --git a/versioned_docs/version-4.0.x/adaptors-spark.md b/versioned_docs/version-4.0.x/adaptors-spark.md
new file mode 100644
index 000000000000..1346a381be03
--- /dev/null
+++ b/versioned_docs/version-4.0.x/adaptors-spark.md
@@ -0,0 +1,83 @@
+---
+id: adaptors-spark
+title: Pulsar adaptor for Apache Spark
+sidebar_label: "Apache Spark"
+description: Get a comprehensive understanding of concepts, configuration methods, and usage of Pulsar adaptor for Apache Spark.
+---
+
+## Spark Streaming receiver
+The Spark Streaming receiver for Pulsar is a custom receiver that enables Apache [Spark Streaming](https://spark.apache.org/streaming/) to receive raw data from Pulsar.
+
+An application can receive data in [Resilient Distributed Dataset](https://spark.apache.org/docs/latest/programming-guide.html#resilient-distributed-datasets-rdds) (RDD) format via the Spark Streaming receiver and can process it in a variety of ways.
+
+### Prerequisites
+
+To use the receiver, include a dependency for the `pulsar-spark` library in your Java configuration.
+
+#### Maven
+
+If you're using Maven, add this to your `pom.xml`:
+
+```xml
+<!-- in your <properties> block -->
+<pulsar.version>@pulsar:version@</pulsar.version>
+
+<!-- in your <dependencies> block -->
+<dependency>
+  <groupId>org.apache.pulsar</groupId>
+  <artifactId>pulsar-spark</artifactId>
+  <version>${pulsar.version}</version>
+</dependency>
+```
+
+#### Gradle
+
+If you're using Gradle, add this to your `build.gradle` file:
+
+```groovy
+def pulsarVersion = "@pulsar:version@"
+
+dependencies {
+    compile group: 'org.apache.pulsar', name: 'pulsar-spark', version: pulsarVersion
+}
+```
+
+### Usage
+
+Pass an instance of `SparkStreamingPulsarReceiver` to the `receiverStream` method in `JavaStreamingContext`:
+
+```java
+    String serviceUrl = "pulsar://localhost:6650/";
+    String topic = "persistent://public/default/test_src";
+    String subs = "test_sub";
+
+    SparkConf sparkConf = new SparkConf().setMaster("local[*]").setAppName("Pulsar Spark Example");
+
+    JavaStreamingContext jsc = new JavaStreamingContext(sparkConf, Durations.seconds(60));
+
+    ConsumerConfigurationData<byte[]> pulsarConf = new ConsumerConfigurationData();
+
+    Set<String> set = new HashSet();
+    set.add(topic);
+    pulsarConf.setTopicNames(set);
+    pulsarConf.setSubscriptionName(subs);
+
+    SparkStreamingPulsarReceiver pulsarReceiver = new SparkStreamingPulsarReceiver(
+        serviceUrl,
+        pulsarConf,
+        new AuthenticationDisabled());
+
+    JavaReceiverInputDStream<byte[]> lineDStream = jsc.receiverStream(pulsarReceiver);
+```
+
+For a complete example, click [here](https://github.com/apache/pulsar-adapters/blob/master/examples/spark/src/main/java/org/apache/spark/streaming/receiver/example/SparkStreamingPulsarReceiverExample.java). In this example, the number of messages that contain the string "Pulsar" in received messages is counted.
+
+Note that if needed, other Pulsar authentication classes can be used. For example, to use a token during authentication the following parameters for the `SparkStreamingPulsarReceiver` constructor can be set:
+
+```java
+SparkStreamingPulsarReceiver pulsarReceiver = new SparkStreamingPulsarReceiver(
+        serviceUrl,
+        pulsarConf,
+        new AuthenticationToken("token:<secret-JWT-token>"));
+```
+
diff --git a/versioned_docs/version-4.0.x/adaptors-storm.md b/versioned_docs/version-4.0.x/adaptors-storm.md
new file mode 100644
index 000000000000..0bbe4b17ecac
--- /dev/null
+++ b/versioned_docs/version-4.0.x/adaptors-storm.md
@@ -0,0 +1,90 @@
+---
+id: adaptors-storm
+title: Pulsar adaptor for Apache Storm
+sidebar_label: "Apache Storm"
+description: Get a comprehensive understanding of Pulsar adaptor for Apache Storm.
+---
+
+Pulsar Storm is an adaptor for integrating with [Apache Storm](http://storm.apache.org/) topologies. It provides core Storm implementations for sending and receiving data.
+
+An application can inject data into a Storm topology via a generic Pulsar spout, as well as consume data from a Storm topology via a generic Pulsar bolt.
+
+## Using the Pulsar Storm Adaptor
+
+To use the Pulsar Storm Adaptor, you need to include dependency for Pulsar Storm Adaptor:
+
+```xml
+<dependency>
+  <groupId>org.apache.pulsar</groupId>
+  <artifactId>pulsar-storm</artifactId>
+  <version>${pulsar.version}</version>
+</dependency>
+```
+
+## Pulsar Spout
+
+The Pulsar Spout allows for the data published on a topic to be consumed by a Storm topology. It emits a Storm tuple based on the message received and the `MessageToValuesMapper` provided by the client.
+
+The tuples that fail to be processed by the downstream bolts will be re-injected by the spout with an exponential backoff, within a configurable timeout (the default is 60 seconds) or a configurable number of retries, whichever comes first, after which it is acknowledged by the consumer. Here's an example construction of a spout:
+
+```java
+MessageToValuesMapper messageToValuesMapper = new MessageToValuesMapper() {
+
+    @Override
+    public Values toValues(Message msg) {
+        return new Values(new String(msg.getData()));
+    }
+
+    @Override
+    public void declareOutputFields(OutputFieldsDeclarer declarer) {
+        // declare the output fields
+        declarer.declare(new Fields("string"));
+    }
+};
+
+// Configure a Pulsar Spout
+PulsarSpoutConfiguration spoutConf = new PulsarSpoutConfiguration();
+spoutConf.setServiceUrl("pulsar://broker.messaging.usw.example.com:6650");
+spoutConf.setTopic("persistent://my-property/usw/my-ns/my-topic1");
+spoutConf.setSubscriptionName("my-subscriber-name1");
+spoutConf.setMessageToValuesMapper(messageToValuesMapper);
+
+// Create a Pulsar Spout
+PulsarSpout spout = new PulsarSpout(spoutConf);
+```
+
+For a complete example, click [here](https://github.com/apache/pulsar-adapters/blob/master/pulsar-storm/src/test/java/org/apache/pulsar/storm/PulsarSpoutTest.java).
+
+## Pulsar Bolt
+
+The Pulsar bolt allows data in a Storm topology to be published on a topic. It publishes messages based on the Storm tuple received and the `TupleToMessageMapper` provided by the client.
+
+A partitioned topic can also be used to publish messages on different topics. In the implementation of the `TupleToMessageMapper`, a "key" will need to be provided in the message which will send the messages with the same key to the same topic. Here's an example bolt:
+
+```java
+TupleToMessageMapper tupleToMessageMapper = new TupleToMessageMapper() {
+
+    @Override
+    public TypedMessageBuilder<byte[]> toMessage(TypedMessageBuilder<byte[]> msgBuilder, Tuple tuple) {
+        String receivedMessage = tuple.getString(0);
+        // message processing
+        String processedMsg = receivedMessage + "-processed";
+        return msgBuilder.value(processedMsg.getBytes());
+    }
+
+    @Override
+    public void declareOutputFields(OutputFieldsDeclarer declarer) {
+        // declare the output fields
+    }
+};
+
+// Configure a Pulsar Bolt
+PulsarBoltConfiguration boltConf = new PulsarBoltConfiguration();
+boltConf.setServiceUrl("pulsar://broker.messaging.usw.example.com:6650");
+boltConf.setTopic("persistent://my-property/usw/my-ns/my-topic2");
+boltConf.setTupleToMessageMapper(tupleToMessageMapper);
+
+// Create a Pulsar Bolt
+PulsarBolt bolt = new PulsarBolt(boltConf);
+```
+
diff --git a/versioned_docs/version-4.0.x/admin-api-brokers.md b/versioned_docs/version-4.0.x/admin-api-brokers.md
new file mode 100644
index 000000000000..65114086fa7d
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-brokers.md
@@ -0,0 +1,267 @@
+---
+id: admin-api-brokers
+title: Managing Brokers
+sidebar_label: "Brokers"
+description: Learn how to manage brokers using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+:::tip
+
+This page only shows **some frequently used operations**. For the latest and complete information, see the **reference docs** below.
+
+:::
+
+Category|Method|If you want to manage brokers...
+|---|---|---
+[Pulsar CLI](reference-cli-tools.md) |[pulsar-admin](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/), which lists all commands, flags, descriptions, and more.| See the `broker` command
+[Pulsar admin APIs](admin-api-overview.md)| {@inject: rest:REST API:/}, which lists all parameters, responses, samples, and more.|See the `/admin/v2/brokers` endpoint
+[Pulsar admin APIs](admin-api-overview.md)|[Java admin API](/api/admin/), which lists all classes, methods, descriptions, and more.|See the `brokers` method of the `PulsarAdmin` object
+
+## List active broker
+
+Fetch all available active brokers that are serving traffic with cluster name.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin brokers list use
+```
+
+Example output:
+
+```
+localhost:8080
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/brokers/:cluster|operation/getActiveBrokers?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().getActiveBrokers(clusterName)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## List namespace owned by broker
+
+You can list all namespaces which are owned and served by a given broker.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin brokers namespaces use \
+    --url localhost:8080
+```
+
+Example output:
+
+```
+public/default/0x00000000_0x40000000    [broker_assignment=shared is_controlled=false is_active=true]
+public/default/0xc0000000_0xffffffff    [broker_assignment=shared is_controlled=false is_active=true]
+public/functions/0x40000000_0x80000000    [broker_assignment=shared is_controlled=false is_active=true]
+public/functions/0x00000000_0x40000000    [broker_assignment=shared is_controlled=false is_active=true]
+pulsar/standalone/localhost:8080/0x00000000_0xffffffff    [broker_assignment=shared is_controlled=false is_active=true]
+pulsar/localhost:8080/0x00000000_0xffffffff    [broker_assignment=shared is_controlled=false is_active=true]
+public/functions/0x80000000_0xc0000000    [broker_assignment=shared is_controlled=false is_active=true]
+public/default/0x80000000_0xc0000000    [broker_assignment=shared is_controlled=false is_active=true]
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/brokers/:cluster/:broker/ownedNamespaces|operation/getOwnedNamespaes?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().getOwnedNamespaces(cluster,brokerUrl);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Update broker conf 
+
+You can update broker configurations using one of the following ways:
+
+- Supply [configurations](pathname:///reference/#/@pulsar:version_reference@/config/reference-configuration-broker) when starting up brokers.
+
+- [Update configurations dynamically](#update-broker-conf-dynamically) **when running brokers**.
+
+  Since all broker configurations in Pulsar are stored in ZooKeeper, configuration values can also be dynamically updated when brokers are running. When you update broker configurations dynamically, ZooKeeper will notify the broker of the change and then the broker will override any existing configuration values.
+
+### List updatable broker conf
+
+Fetch a list of all potentially updatable configuration parameters.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin brokers list-dynamic-config
+```
+
+Example output:
+
+```
+forceDeleteNamespaceAllowed
+loadBalancerMemoryResourceWeight
+allowAutoTopicCreation
+brokerDeleteInactivePartitionedTopicMetadataEnabled
+managedLedgerInactiveLedgerRolloverTimeSeconds
+loadBalancerNamespaceBundleMaxMsgRate
+resourceUsageTransportPublishIntervalInSecs
+# omit...
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/brokers/configuration|operation/getDynamicConfigurationName?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().getDynamicConfigurationNames();
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Update broker conf dynamically
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+The [`update-dynamic-config`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/brokers?id=update-dynamic-config) subcommand will update existing configuration. It takes two arguments: the name of the parameter and the new value using the `config` and `value` flag respectively. Here's an example of the [`brokerShutdownTimeoutMs`](reference-configuration.md#broker-brokerShutdownTimeoutMs) parameter:
+
+```shell
+pulsar-admin brokers update-dynamic-config --config brokerShutdownTimeoutMs --value 100
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/brokers/configuration/:configName/:configValue|operation/updateDynamicConfiguration?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().updateDynamicConfiguration(configName, configValue);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List updated broker conf
+
+Fetch a list of all parameters that have been dynamically updated.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin brokers get-all-dynamic-config
+```
+Example output:
+
+```
+brokerShutdownTimeoutMs    100
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/brokers/configuration/values|operation/getAllDynamicConfigurations?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().getAllDynamicConfigurations();
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get info of leader broker
+
+Fetch the information of the leader broker, for example, the service URL.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin brokers leader-broker
+```
+
+Example output:
+
+```json
+{
+  "serviceUrl" : "http://localhost:8080"
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/brokers/leaderBroker|operation/getLeaderBroker?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.brokers().getLeaderBroker()
+```
+
+For the detail of the code above, see [here](https://github.com/apache/pulsar/blob/master/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/BrokersImpl.java#L80)
+
+</TabItem>
+
+</Tabs>
+````
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-clusters.md b/versioned_docs/version-4.0.x/admin-api-clusters.md
new file mode 100644
index 000000000000..e632d96790c4
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-clusters.md
@@ -0,0 +1,265 @@
+---
+id: admin-api-clusters
+title: Managing Clusters
+sidebar_label: "Clusters"
+description: Learn how to manage clusters using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+:::tip
+
+This page only shows **some frequently used operations**. For the latest and complete information, see the **reference docs** below.
+
+:::
+
+Category|Method|If you want to manage clusters...
+|---|---|---
+[Pulsar CLI](reference-cli-tools.md) |[pulsar-admin](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/), which lists all commands, flags, descriptions, and more.| See the `clusters` command
+[Pulsar admin APIs](admin-api-overview.md)| {@inject: rest:REST API:/}, which lists all parameters, responses, samples, and more.|See the `/admin/v2/clusters` endpoint
+[Pulsar admin APIs](admin-api-overview.md)|[Java admin API](/api/admin/), which lists all classes, methods, descriptions, and more.|See the `clusters` method of the `PulsarAdmin` object
+
+You can perform the following operations on [clusters](reference-terminology.md#cluster).
+
+## Provision cluster
+
+You can provision new clusters using the admin interface.
+
+:::note
+
+- This operation requires superuser privileges.
+
+- When provisioning a new cluster, you need to [initialize cluster metadata](deploy-bare-metal.md#initialize-cluster-metadata). Cluster metadata can be initialized through the pulsar-admin CLI **only**. It cannot be performed via Pulsar admin APIs (REST API and Java admin API).
+
+:::
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+You can provision a new cluster using the [`create`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=create) subcommand. Here's an example:
+
+```shell
+pulsar-admin clusters create cluster-1 \
+    --url http://my-cluster.org.com:8080 \
+    --broker-url pulsar://my-cluster.org.com:6650
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/clusters/:cluster|operation/createCluster?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+ClusterData clusterData = new ClusterData(
+        serviceUrl,
+        serviceUrlTls,
+        brokerServiceUrl,
+        brokerServiceUrlTls
+);
+admin.clusters().createCluster(clusterName, clusterData);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get cluster configuration
+
+You can fetch the [configuration](reference-configuration.md) for an existing cluster at any time.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`get`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=get) subcommand and specify the name of the cluster. Here's an example:
+
+```shell
+pulsar-admin clusters get cluster-1
+```
+Output:
+
+```json
+{
+    "serviceUrl": "http://my-cluster.org.com:8080/",
+    "serviceUrlTls": null,
+    "brokerServiceUrl": "pulsar://my-cluster.org.com:6650/",
+    "brokerServiceUrlTls": null
+    "peerClusterNames": null
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/clusters/:cluster|operation/getCluster?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.clusters().getCluster(clusterName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Update cluster
+
+### Update cluster configuration
+
+You can update the configuration for an existing cluster at any time.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`update`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=update) subcommand and specify new configuration values using flags.
+
+```shell
+pulsar-admin clusters update cluster-1 \
+    --url http://my-cluster.org.com:4081 \
+    --broker-url pulsar://my-cluster.org.com:3350
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/clusters/:cluster|operation/updateCluster?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+ClusterData clusterData = new ClusterData(
+        serviceUrl,
+        serviceUrlTls,
+        brokerServiceUrl,
+        brokerServiceUrlTls
+);
+admin.clusters().updateCluster(clusterName, clusterData);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Update peer-cluster data
+
+Peer clusters can be configured for a given cluster in a Pulsar [instance](reference-terminology.md#instance).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`update-peer-clusters`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=update) subcommand and specify the list of peer-cluster names.
+
+```shell
+pulsar-admin update-peer-clusters cluster-1 --peer-clusters cluster-2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/clusters/:cluster/peers|operation/setPeerClusterNames?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.clusters().updatePeerClusterNames(clusterName, peerClusterList);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## List cluster
+
+You can fetch a list of all clusters in a Pulsar [instance](reference-terminology.md#instance).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`list`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=list) subcommand.
+
+```shell
+pulsar-admin clusters list
+```
+
+Output:
+
+```
+cluster-1
+cluster-2
+```
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/clusters|operation/getClusters?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.clusters().getClusters();
+```
+
+</TabItem>
+
+</Tabs>
+````
+## Delete cluster
+
+Clusters can be deleted from a Pulsar [instance](reference-terminology.md#instance).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`delete`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=delete) subcommand and specify the name of the cluster.
+
+```
+pulsar-admin clusters delete cluster-1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/clusters/:cluster|operation/deleteCluster?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.clusters().deleteCluster(clusterName);
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-features.md b/versioned_docs/version-4.0.x/admin-api-features.md
new file mode 100644
index 000000000000..0ba81cac47b5
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-features.md
@@ -0,0 +1,41 @@
+---
+id: admin-api-features
+title: Pulsar admin interfaces - Features
+sidebar_label: "Features"
+description: Get a comprehensive understanding of features of Pulsar admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+Below are the most common tasks you may want to do. For the exhaustive lists of tasks and the method to perform these tasks, see [Tools](admin-api-tools.md).
+
+![Features of Pulsar admin APIs](/assets/admin-api-features.svg)
+
+These administrative tasks are categorized based on Pulsar components.
+
+Category |Components|What do you want to do?
+|---|---|---
+Server|Broker|Operations on brokers.<br/><br/>For example, <br/>- Set dynamic configurations on brokers<br/> - Run health checks against brokers<br/> - Shutdown brokers<br/> - Get broker-level stats metrics
+Storage|Bookie|Operations on bookie placement policy. <br/><br/>For example, <br/> - Get or set bookie replacement policy
+Entities|- Topic <br/><br/> - Schema <br/><br/> - Namespace  <br/><br/> - Tenant <br/><br/> - Cluster|Operations on topics, schemas, namespaces, tenants, or clusters.<br/><br/> For example, <br/> - Create, update or delete topics, tenants, namespaces, or clusters <br/> - Set isolation policies, configure offload thresholds, or set permissions for namespaces <br/> - Upload, extract, or delete schemas
+Stream processing|<br/><br/> - Function <br/><br/> - Connector <br/><br/> - Transaction <br/><br/> - Package | Operations on functions, function workers, or connectors. <br/> For example, <br/> - Create, update, and delete functions or connectors <br/> - Get stats of function workers, trigger rebalance of functions to workers <br/><br/> Operations on transactions. <br/> For example, <br/> - Get stats of transactions <br/> - Update the scale of transaction coordinators <br/><br/> Operatio [...]
+Others| - Proxy <br/><br/> - Resource groups <br/><br/> - Resource quotas | Operations on proxy stats. <br/> For example, <br/> - Get various monitoring metrics for proxy stats <br/><br/> Operations on resource groups.<br/>For example, <br/> - Create, update, and delete resource groups <br/><br/>Operations on resource quotas.<br/>For example, <br/> - Set resource quota for namespace bundles
+
+### Related topics
+
+- To understand the basics, see [Pulsar admin API - Overview](admin-api-overview.md)
+
+- To learn usage scenarios, see [Pulsar admin API - Use cases](admin-api-use-cases.md).
+
+- To perform administrative operations, see [Pulsar admin API - Tools](admin-api-tools.md).
+
+- To get up quickly, see [Pulsar admin API - Get started](admin-get-started.md).
+
+- To check the detailed usage, see the API references below.
+
+  - [Java admin API](/api/admin/)
+
+  - [REST API](reference-rest-api-overview.md)
diff --git a/versioned_docs/version-4.0.x/admin-api-functions.md b/versioned_docs/version-4.0.x/admin-api-functions.md
new file mode 100644
index 000000000000..72d925f2caed
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-functions.md
@@ -0,0 +1,764 @@
+---
+id: admin-api-functions
+title: Manage Functions
+sidebar_label: "Functions"
+description: Learn how to manage functions using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+:::tip
+
+This page only shows **some frequently used operations**. For the latest and complete information, see the **reference docs** below.
+
+:::
+
+Category|Method|If you want to manage functions...
+|---|---|---
+[Pulsar CLI](reference-cli-tools.md) |[pulsar-admin](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/), which lists all commands, flags, descriptions, and more.| See the `functions` command
+[Pulsar admin APIs](admin-api-overview.md)| {@inject: rest:REST API:/}, which lists all parameters, responses, samples, and more.|See the `/admin/v3/functions` endpoint
+[Pulsar admin APIs](admin-api-overview.md)|[Java admin API](/api/admin/), which lists all classes, methods, descriptions, and more.|See the `functions` method of the `PulsarAdmin` object
+
+
+You can perform the following operations on [functions](functions-overview.md/#what-are-pulsar-functions).
+## Create a function
+
+You can create a Pulsar function in cluster mode (deploy it on a Pulsar cluster) using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`create`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=create) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions create \
+    --tenant public \
+    --namespace default \
+    --name (the name of Pulsar Functions) \
+    --inputs test-input-topic \
+    --output persistent://public/default/test-output-topic \
+    --classname org.apache.pulsar.functions.api.examples.ExclamationFunction \
+    --jar $PWD/examples/api-examples.jar
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName|operation/registerFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+FunctionConfig functionConfig = new FunctionConfig();
+functionConfig.setTenant(tenant);
+functionConfig.setNamespace(namespace);
+functionConfig.setName(functionName);
+functionConfig.setRuntime(FunctionConfig.Runtime.JAVA);
+functionConfig.setParallelism(1);
+functionConfig.setClassName("org.apache.pulsar.functions.api.examples.ExclamationFunction");
+functionConfig.setProcessingGuarantees(FunctionConfig.ProcessingGuarantees.ATLEAST_ONCE);
+functionConfig.setTopicsPattern(sourceTopicPattern);
+functionConfig.setSubName(subscriptionName);
+functionConfig.setAutoAck(true);
+functionConfig.setOutput(sinkTopic);
+admin.functions().createFunction(functionConfig, fileName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Update a function
+
+You can update a Pulsar function that has been deployed to a Pulsar cluster using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`update`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=update) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions update \
+    --tenant public \
+    --namespace default \
+    --name (the name of Pulsar Functions) \
+    --output persistent://public/default/update-output-topic \
+    # other options
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v3/functions/:tenant/:namespace/:functionName|operation/updateFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+FunctionConfig functionConfig = new FunctionConfig();
+functionConfig.setTenant(tenant);
+functionConfig.setNamespace(namespace);
+functionConfig.setName(functionName);
+functionConfig.setRuntime(FunctionConfig.Runtime.JAVA);
+functionConfig.setParallelism(1);
+functionConfig.setClassName("org.apache.pulsar.functions.api.examples.ExclamationFunction");
+UpdateOptions updateOptions = new UpdateOptions();
+updateOptions.setUpdateAuthData(updateAuthData);
+admin.functions().updateFunction(functionConfig, userCodeFile, updateOptions);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Start a function
+
+You can [start an instance of a function](#start-an-instance-of-a-function) or [start all instances of a function](#start-all-instances-of-a-function).
+### Start an instance of a function
+
+You can start a stopped function instance with `instance-id` using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`start`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=start) subcommand.
+
+```shell
+pulsar-admin functions start \
+    --tenant public \
+    --namespace default \
+    --name (the name of Pulsar Functions) \
+    --instance-id 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/:instanceId/start|operation/startFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().startFunction(tenant, namespace, functionName, Integer.parseInt(instanceId));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Start all instances of a function
+
+You can start all stopped function instances using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`start`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=start) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions start \
+    --tenant public \
+    --namespace default \
+    --name (the name of Pulsar Functions) \
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/start|operation/startFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().startFunction(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Stop a function
+
+You can [stop an instance of a function](#stop-an-instance-of-a-function) or [stop all instances of a function](#stop-all-instances-of-a-function).
+
+### Stop an instance of a function
+
+You can stop a function instance with `instance-id` using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`stop`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=stop) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions stop \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--instance-id 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/:instanceId/stop|operation/stopFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().stopFunction(tenant, namespace, functionName, Integer.parseInt(instanceId));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Stop all instances of a function
+
+You can stop all function instances using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`stop`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=stop) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions stop \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/stop|operation/stopFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().stopFunction(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Restart a function
+
+You can [restart an instance of a function](#restart-an-instance-of-a-function) or [restart all instances of a function](#restart-all-instances-of-a-function).
+
+### Restart an instance of a function
+
+Restart a function instance with `instance-id` using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`restart`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=restart) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions restart \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--instance-id 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/:instanceId/restart|operation/restartFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().restartFunction(tenant, namespace, functionName, Integer.parseInt(instanceId));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Restart all instances of a function
+
+You can restart all function instances using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`restart`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=restart) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions restart \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/restart|operation/restartFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().restartFunction(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## List all functions
+
+You can list all Pulsar functions running under a specific tenant and namespace using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`list`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=list) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions list \
+	--tenant public \
+	--namespace default
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace|operation/listFunctions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctions(tenant, namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Delete a function
+
+You can delete a Pulsar function that is running on a Pulsar cluster using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`delete`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=delete) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions delete \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v3/functions/:tenant/:namespace/:functionName|operation/deregisterFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().deleteFunction(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get info about a function
+
+You can get information about a Pulsar function currently running in cluster mode using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`get`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=get) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions get \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName|operation/getFunctionInfo?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunction(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get status of a function
+
+You can [get the status of an instance of a function](#get-status-of-an-instance-of-a-function) or [get the status of all instances of a function](#get-status-of-all-instances-of-a-function).
+
+### Get status of an instance of a function
+
+You can get the current status of a Pulsar function instance with `instance-id` using Admin CLI, REST API or Java Admin API.
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`status`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=status) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions status \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--instance-id 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName/:instanceId/status|operation/getFunctionInstanceStatus?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctionStatus(tenant, namespace, functionName, Integer.parseInt(instanceId));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get status of all instances of a function
+
+You can get the current status of a Pulsar function instance using Admin CLI, REST API or Java Admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`status`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=status) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions status \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName/status|operation/getFunctionStatus?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctionStatus(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get stats of a function
+
+You can [get stats of an instance of a function](#get-stats-of-an-instance-of-a-function) or [get stats of all instances of a function](#get-stats-of-all-instances-of-a-function).
+### Get stats of an instance of a function
+
+You can get the current stats of a Pulsar Function instance with `instance-id` using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`stats`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=stats) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions stats \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--instance-id 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName/:instanceId/stats|operation/getFunctionInstanceStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctionStats(tenant, namespace, functionName, Integer.parseInt(instanceId));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get stats of all instances of a function
+
+You can get the current stats of a Pulsar function using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`stats`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=stats) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions stats \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName/stats|operation/getFunctionStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctionStats(tenant, namespace, functionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Trigger a function
+
+You can trigger a specified Pulsar function with a supplied value using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`trigger`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=trigger) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions trigger \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--topic (the name of input topic) \
+	--trigger-value \"hello pulsar\"
+	# or --trigger-file (the path of trigger file)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/trigger|operation/triggerFunction?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().triggerFunction(tenant, namespace, functionName, topic, triggerValue, triggerFile);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+
+## Put state associated with a function
+
+You can put the state associated with a Pulsar function using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`putstate`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=putstate) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions putstate \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--state "{\"key\":\"pulsar\", \"stringValue\":\"hello pulsar\"}"
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/functions/:tenant/:namespace/:functionName/state/:key|operation/putFunctionState?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+TypeReference<FunctionState> typeRef = new TypeReference<FunctionState>() {};
+FunctionState stateRepr = ObjectMapperFactory.getThreadLocal().readValue(state, typeRef);
+admin.functions().putFunctionState(tenant, namespace, functionName, stateRepr);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Fetch state associated with a function
+
+You can fetch the current state associated with a Pulsar function using Admin CLI, REST API or Java admin API.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="Admin CLI">
+
+Use the [`querystate`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/functions?id=querystate) subcommand.
+
+**Example**
+
+```shell
+pulsar-admin functions querystate \
+	--tenant public \
+	--namespace default \
+	--name (the name of Pulsar Functions) \
+	--key (the key of state)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/functions/:tenant/:namespace/:functionName/state/:key|operation/getFunctionState?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.functions().getFunctionState(tenant, namespace, functionName, key);
+```
+
+</TabItem>
+
+</Tabs>
+````
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-namespaces.md b/versioned_docs/version-4.0.x/admin-api-namespaces.md
new file mode 100644
index 000000000000..6624f9d95c89
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-namespaces.md
@@ -0,0 +1,1254 @@
+---
+id: admin-api-namespaces
+title: Managing Namespaces
+sidebar_label: "Namespaces"
+description: Learn how to manage namespaces using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+ This page only shows **some frequently used operations**.
+
+ - For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more information, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+ - For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+ - For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+Pulsar [namespaces](reference-terminology.md#namespace) are logical groupings of [topics](reference-terminology.md#topic).
+
+Namespaces can be managed via:
+
+* The `namespaces` command of the [`pulsar-admin`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/) tool
+* The `/admin/v2/namespaces` endpoint of the admin {@inject: rest:REST:/} API
+* The `namespaces` method of the `PulsarAdmin` object in the [Java API](client-libraries-java.md)
+
+## Namespaces resources
+
+### Create namespaces
+
+You can create new namespaces under a given [tenant](reference-terminology.md#tenant).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`create`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=create) subcommand and specify the namespace by name:
+
+```shell
+pulsar-admin namespaces create test-tenant/test-namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/namespaces/:tenant/:namespace|operation/createNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().createNamespace(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get policies
+
+You can fetch the current policies associated with a namespace at any time.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`policies`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=policies) subcommand and specify the namespace:
+
+```shell
+pulsar-admin namespaces policies test-tenant/test-namespace
+```
+
+Example output:
+
+```json
+{
+  "auth_policies": {
+    "namespace_auth": {},
+    "destination_auth": {}
+  },
+  "replication_clusters": [],
+  "bundles_activated": true,
+  "bundles": {
+    "boundaries": [
+      "0x00000000",
+      "0xffffffff"
+    ],
+    "numBundles": 1
+  },
+  "backlog_quota_map": {},
+  "persistence": null,
+  "latency_stats_sample_rate": {},
+  "message_ttl_in_seconds": 0,
+  "retention_policies": null,
+  "deleted": false
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace|operation/getPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getPolicies(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List namespaces
+
+You can list all namespaces within a given Pulsar [tenant](reference-terminology.md#tenant).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`list`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=list) subcommand and specify the tenant:
+
+```shell
+pulsar-admin namespaces list test-tenant
+```
+
+Example output:
+
+```
+test-tenant/namespace1
+test-tenant/namespace2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant|operation/getTenantNamespaces?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getNamespaces(tenant);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete namespaces
+
+You can delete existing namespaces from a tenant.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`delete`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=delete) subcommand and specify the namespace:
+
+```shell
+pulsar-admin namespaces delete test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace|operation/deleteNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().deleteNamespace(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure replication clusters
+
+#### Set replication cluster
+
+You can set replication clusters for a namespace to enable Pulsar to internally replicate the published messages from one colocation facility to another.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-clusters test-tenant/namespace1 --clusters cl1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/replication|operation/setNamespaceReplicationClusters?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setNamespaceReplicationClusters(namespace, clusters);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get replication cluster
+
+You can get the list of replication clusters for a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-clusters test-tenant/cluster1/namespace1
+```
+
+Example output:
+
+```
+cluster2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/replication|operation/getNamespaceReplicationClusters?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getNamespaceReplicationClusters(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure backlog quota policies
+
+#### Set backlog quota policies
+
+Backlog quota helps the broker to restrict bandwidth/storage of a namespace once it reaches a certain threshold limit. Admin can set the limit and take the corresponding action after the limit is reached.
+
+  1.  producer_request_hold: the producer holds the message and retries until client configuration `sendTimeoutMs` is exceeded
+
+  2.  producer_exception: the producer throws an exception when trying to send a message
+
+  3.  consumer_backlog_eviction: broker starts discarding backlog messages
+
+Backlog quota restriction can be taken care of by defining the restriction of backlog-quota-type: destination_storage.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-backlog-quota --limit 10G \
+    --limitTime 36000 \
+    --policy producer_request_hold \
+    test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/backlogQuota|operation/setBacklogQuota?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setBacklogQuota(namespace, new BacklogQuota(limit, limitTime, policy))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get backlog quota policies
+
+You can get a configured backlog quota for a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-backlog-quotas test-tenant/namespace1
+```
+
+Example output:
+
+```
+destination_storage    BacklogQuotaImpl(limit=10737418240, limitSize=10737418240, limitTime=36000, policy=producer_request_hold)
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/backlogQuotaMap|operation/getBacklogQuotaMap?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getBacklogQuotaMap(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove backlog quota policies
+
+You can remove backlog quota policies for a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces remove-backlog-quota test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/backlogQuota|operation/removeBacklogQuota?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().removeBacklogQuota(namespace, backlogQuotaType)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure persistence policies
+
+#### Set persistence policies
+
+Persistence policies allow users to configure persistency-level for all topic messages under a given namespace.
+
+  -   Bookkeeper-ack-quorum: Number of acks (guaranteed copies) to wait for each entry, default: 2
+
+  -   Bookkeeper-ensemble: Number of bookies to use for a topic, default: 2
+
+  -   Bookkeeper-write-quorum: How many writes to make of each entry, default: 2
+
+  -   Ml-mark-delete-max-rate: Throttling rate of mark-delete operation (0 means no throttle), default: 0
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-persistence \
+    --bookkeeper-ack-quorum 2 --bookkeeper-ensemble 3 \
+    --bookkeeper-write-quorum 2 --ml-mark-delete-max-rate 0 \
+    test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/persistence|operation/setPersistence?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setPersistence(namespace,new PersistencePolicies(bookkeeperEnsemble, bookkeeperWriteQuorum,bookkeeperAckQuorum,managedLedgerMaxMarkDeleteRate))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get persistence policies
+
+You can get the configured persistence policies of a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-persistence test-tenant/namespace1
+```
+
+Example output:
+
+```json
+{
+  "bookkeeperEnsemble": 3,
+  "bookkeeperWriteQuorum": 2,
+  "bookkeeperAckQuorum": 2,
+  "managedLedgerMaxMarkDeleteRate": 0
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/persistence|operation/getPersistence?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getPersistence(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure namespace bundles
+
+#### Unload namespace bundles
+
+A namespace bundle is a virtual group of topics that belong to the same namespace. If the broker gets overloaded with the number of bundles, this command can help unload a bundle from that broker, so it can be served by some other less-loaded brokers. The namespace bundle ID ranges from 0x00000000 to 0xffffffff.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces unload --bundle 0x00000000_0xffffffff test-tenant/namespace1
+pulsar-admin namespaces unload --bundle 0x00000000_0xffffffff test-tenant/namespace1
+pulsar-admin namespaces unload --bundle 0x00000000_0xffffffff --destinationBroker broker1.use.org.com:8080 test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/namespaces/:tenant/:namespace/:bundle/unload|operation/unloadNamespaceBundle?version=@pulsar:version_number@&destinationBroker=broker1.use.org.com:8080}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().unloadNamespaceBundle(namespace, bundle)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Split namespace bundles
+
+One namespace bundle can contain multiple topics but can be served by only one broker. If a single bundle is creating an excessive load on a broker, an admin can split the bundle using the command below, permitting one or more of the new bundles to be unloaded, thus balancing the load across the brokers.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces split-bundle --bundle 0x00000000_0xffffffff test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/namespaces/:tenant/:namespace/:bundle/split|operation/splitNamespaceBundle?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().splitNamespaceBundle(namespace, bundle, unloadSplitBundles, splitAlgorithmName)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure message TTL
+
+#### Set message-ttl
+
+You can configure the time to live (in seconds) duration for messages. In the example below, the message-ttl is set as 100s.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-message-ttl --messageTTL 100 test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/messageTTL|operation/setNamespaceMessageTTL?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setNamespaceMessageTTL(namespace, messageTTL)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get message-ttl
+
+When the message-ttl for a namespace is set, you can use the command below to get the configured value. This example comtinues the example of the command `set message-ttl`, so the returned value is 100(s).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-message-ttl test-tenant/namespace1
+```
+Example output:
+
+```
+100
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/messageTTL|operation/getNamespaceMessageTTL?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getNamespaceMessageTTL(namespace)
+```
+
+```
+100
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove message-ttl
+
+Remove a message TTL of the configured namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces remove-message-ttl test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/messageTTL|operation/removeNamespaceMessageTTL?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().removeNamespaceMessageTTL(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+### Clear backlog
+
+#### Clear namespace backlog
+
+It clears all message backlogs for all the topics that belong to a specific namespace. You can also clear backlogs for a specific subscription as well.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces clear-backlog --sub my-subscription test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/clearBacklog|operation/clearNamespaceBacklogForSubscription?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().clearNamespaceBacklogForSubscription(namespace, subscription)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Clear bundle backlog
+
+It clears all message backlogs for all the topics that belong to a specific NamespaceBundle. You can also clear backlogs for a specific subscription as well.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces clear-backlog \
+    --bundle 0x00000000_0xffffffff \
+    --sub my-subscription \
+    test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/:bundle/clearBacklog|operation/clearNamespaceBundleBacklogForSubscription?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+
+admin.namespaces().clearNamespaceBundleBacklogForSubscription(namespace, bundle, subscription)
+
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure retention
+
+#### Set retention
+
+Each namespace contains multiple topics and the retention size (storage size) of each topic should not exceed a specific threshold or it should be stored for a certain period. This command helps configure the retention size and time of topics in a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-retention --size 100M --time 10m test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/retention|operation/setRetention?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setRetention(namespace, new RetentionPolicies(retentionTimeInMin, retentionSizeInMB))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get retention
+
+It shows the retention information of a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-retention test-tenant/namespace1
+```
+
+```json
+{
+  "retentionTimeInMinutes": 10,
+  "retentionSizeInMB": 100
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/retention|operation/getRetention?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getRetention(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure dispatch throttling for topics
+
+#### Set dispatch throttling for topics
+
+It sets the message dispatch rate for all the topics under a given namespace. The dispatch rate can be restricted by the number of messages per X seconds (`msg-dispatch-rate`) or by the number of message-bytes per X second (`byte-dispatch-rate`). The dispatch rate is in second and it can be configured with `dispatch-rate-period`. The default value of `msg-dispatch-rate` and `byte-dispatch-rate` is -1 which
+disables the throttling.
+
+:::note
+
+- If neither `clusterDispatchRate` nor `topicDispatchRate` is configured, dispatch throttling is disabled.
+- If `topicDispatchRate` is not configured, `clusterDispatchRate` takes effect.
+- If `topicDispatchRate` is configured, `topicDispatchRate` takes effect.
+
+:::
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-dispatch-rate test-tenant/namespace1 \
+    --msg-dispatch-rate 1000 \
+    --byte-dispatch-rate 1048576 \
+    --dispatch-rate-period 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/dispatchRate|operation/setDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setDispatchRate(namespace, new DispatchRate(1000, 1048576, 1))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get configured message-rate for topics
+
+It shows the configured message-rate for the namespace (topics under this namespace can dispatch this many messages per second)
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-dispatch-rate test-tenant/namespace1
+```
+
+Example output:
+
+```json
+{
+  "dispatchThrottlingRateInMsg" : 1000,
+  "dispatchThrottlingRateInByte" : 1048576,
+  "relativeToPublishRate" : false,
+  "ratePeriodInSecond" : 1
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/dispatchRate|operation/getDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getDispatchRate(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure dispatch throttling for subscription
+
+#### Set dispatch throttling for subscription
+
+It sets the message dispatch rate for all the subscriptions of topics under a given namespace. The dispatch rate can be restricted by the number of messages per X seconds (`msg-dispatch-rate`) or by the number of message-bytes per X second (`byte-dispatch-rate`). The dispatch rate is in second and it can be configured with `dispatch-rate-period`. The default value of `msg-dispatch-rate` and `byte-dispatch-rate` is -1 which
+disables the throttling.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-subscription-dispatch-rate test-tenant/namespace1 \
+    --msg-dispatch-rate 1000 \
+    --byte-dispatch-rate 1048576 \
+    --dispatch-rate-period 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/subscriptionDispatchRate|operation/setSubscriptionDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setSubscriptionDispatchRate(namespace, new DispatchRate(1000, 1048576, 1))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get configured message-rate for subscription
+
+It shows the configured message-rate for the namespace (topics under this namespace can dispatch this many messages per second).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-subscription-dispatch-rate test-tenant/namespace1
+```
+
+Example output:
+
+```json
+{
+  "dispatchThrottlingRateInMsg" : 1000,
+  "dispatchThrottlingRateInByte" : 1048576,
+  "relativeToPublishRate" : false,
+  "ratePeriodInSecond" : 1
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/subscriptionDispatchRate|operation/getSubscriptionDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getSubscriptionDispatchRate(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure dispatch throttling for replicators
+
+#### Set dispatch throttling for replicators
+
+It sets the message dispatch rate for all the replicators between replication clusters under a given namespace. The dispatch rate can be restricted by the number of messages per X seconds (`msg-dispatch-rate`) or by the number of message-bytes per X second (`byte-dispatch-rate`). The dispatch rate is in second and it can be configured with `dispatch-rate-period`. The default value of `msg-dispatch-rate` and `byte-dispatch-rate` is -1 which disables the throttling.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-replicator-dispatch-rate test-tenant/namespace1 \
+    --msg-dispatch-rate 1000 \
+    --byte-dispatch-rate 1048576 \
+    --dispatch-rate-period 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/replicatorDispatchRate|operation/setDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setReplicatorDispatchRate(namespace, new DispatchRate(1000, 1048576, 1))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get configured message-rate for replicators
+
+It shows the configured message-rate for the namespace (topics under this namespace can dispatch this many messages per second)
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-replicator-dispatch-rate test-tenant/namespace1
+```
+
+```json
+{
+  "dispatchThrottlingRatePerTopicInMsg" : 1000,
+  "dispatchThrottlingRatePerTopicInByte" : 1048576,
+  "ratePeriodInSecond" : 1
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/replicatorDispatchRate|operation/getDispatchRate?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getReplicatorDispatchRate(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Configure deduplication snapshot interval
+
+#### Get deduplication snapshot interval
+
+It shows the configured `deduplicationSnapshotInterval` for a namespace (Each topic under the namespace will take a deduplication snapshot according to this interval)
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-deduplication-snapshot-interval test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/deduplicationSnapshotInterval|operation/getDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getDeduplicationSnapshotInterval(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Set deduplication snapshot interval
+
+Set the configured `deduplicationSnapshotInterval` for a namespace. Each topic under the namespace will take a deduplication snapshot according to this interval. `brokerDeduplicationEnabled` must be set to `true` for this property to take effect.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces set-deduplication-snapshot-interval test-tenant/namespace1 --interval 1000
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/deduplicationSnapshotInterval|operation/setDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setDeduplicationSnapshotInterval(namespace, 1000)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove deduplication snapshot interval
+
+Remove configured `deduplicationSnapshotInterval` of a namespace (Each topic under the namespace will take a deduplication snapshot according to this interval).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces remove-deduplication-snapshot-interval test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/deduplicationSnapshotInterval|operation/deleteDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().removeDeduplicationSnapshotInterval(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Namespace isolation
+
+You can use the [Pulsar isolation policy](administration-isolation.md) to allocate resources (broker and bookie) for a namespace.
+
+### Unload namespaces from a broker
+
+You can unload a namespace, or a [namespace bundle](reference-terminology.md#namespace-bundle), from the Pulsar [broker](reference-terminology.md#broker) that is currently responsible for it.
+
+#### pulsar-admin
+
+Use the [`unload`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=unload) subcommand of the [`namespaces`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces) command.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces unload my-tenant/my-ns
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/namespaces/:tenant/:namespace/unload|operation/unloadNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().unload(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
+### Configure entry filters policy
+
+#### Set entry filters policy
+
+Entry filter helps with filtering message on server side.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```
+pulsar-admin namespaces set-entry-filters \
+    --desc "The description of the entry filter to be used for user help." \
+    --entry-filters-name "The class name for the entry filter." \
+    --entry-filters-dir "The directory for all the entry filter implementations." \
+    test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/entryFilters|operation/setEntryFilters?
+version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().setEntryFilters(namespace, new EntryFilters("desc", "classes name", "class files localtion"))
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Get entry filters policies
+
+You can get a configured entry filter for a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces get-entry-filters test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/entryFilters|operation/getEntryFilters?
+version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getEntryFilters(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove entry filters policies
+
+You can remove entry filters policies for a given namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin namespaces remove-entry-filters test-tenant/namespace1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/entryFilters|operation/removeEntryFilters?
+version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().removeEntryFilters(namespace)
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-non-partitioned-topics.md b/versioned_docs/version-4.0.x/admin-api-non-partitioned-topics.md
new file mode 100644
index 000000000000..b6fedac24225
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-non-partitioned-topics.md
@@ -0,0 +1,7 @@
+---
+id: admin-api-non-partitioned-topics
+title: Managing non-partitioned topics
+sidebar_label: "Non-partitioned topics"
+---
+
+For details of the content, refer to [manage topics](admin-api-topics.md).
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-non-persistent-topics.md b/versioned_docs/version-4.0.x/admin-api-non-persistent-topics.md
new file mode 100644
index 000000000000..2c645e507cae
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-non-persistent-topics.md
@@ -0,0 +1,7 @@
+---
+id: admin-api-non-persistent-topics
+title: Managing non-persistent topics
+sidebar_label: "Non-Persistent topics"
+---
+
+For details of the content, refer to [manage topics](admin-api-topics.md).
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-overview.md b/versioned_docs/version-4.0.x/admin-api-overview.md
new file mode 100644
index 000000000000..4131f1d4db41
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-overview.md
@@ -0,0 +1,31 @@
+---
+id: admin-api-overview
+title: Pulsar admin interfaces
+sidebar_label: "Overview"
+description: Get a comprehensive understanding of usage of Pulsar admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+Pulsar admin APIs enable you to administer clusters programmatically. For example, you can create, update, delete, and manage all the entities within Pulsar instances (such as clusters, namespaces, tenants, topics, schemas, connectors, functions, and so on), and set various policies for data, resources, and security.
+
+![Defination of Pulsar admin APIs](/assets/admin-api-definition.svg)
+
+### Related topics
+
+- To learn usage scenarios, see [Pulsar admin API - Use cases](admin-api-use-cases.md).
+
+- To learn common administrative tasks, see [Pulsar admin API - Features](admin-api-features.md).
+
+- To perform administrative operations, see [Pulsar admin API - Tools](admin-api-tools.md).
+
+- To get up quickly, see [Pulsar admin API - Get started](admin-get-started.md).
+
+- To check the detailed usage, see the API references below.
+
+  - [Java admin API](/api/admin/)
+
+  - [REST API](reference-rest-api-overview.md)
diff --git a/versioned_docs/version-4.0.x/admin-api-packages.md b/versioned_docs/version-4.0.x/admin-api-packages.md
new file mode 100644
index 000000000000..01bbfffe57cf
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-packages.md
@@ -0,0 +1,363 @@
+---
+id: admin-api-packages
+title: Manage packages
+sidebar_label: "Packages"
+description: Learn how to manage packages using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+ This page only shows **some frequently used operations**.
+
+ - For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin doc](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+ - For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+ - For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+Package managers or package-management systems automatically manage packages in a consistent manner. These tools simplify the installation tasks, upgrade process, and deletion operations for users. A package is a minimal unit that a package manager deals with. In Pulsar, packages are organized at the tenant-level and namespace-level to manage Pulsar Functions and Pulsar IO connectors (i.e., source and sink).
+
+## What is a package?
+
+A package is a set of elements that the user would like to reuse in later operations. In Pulsar, a package can be a group of functions, sources, and sinks. You can define a package according to your needs.
+
+The package management system in Pulsar stores the data and metadata of each package (as shown in the table below) and tracks the package versions.
+
+|Metadata|Description|
+|--|--|
+|description|The description of the package.|
+|contact|The contact information of a package. For example, an email address of the developer team.|
+|create_time|The time when the package is created.|
+|modification_time|The time when the package is lastly modified.|
+|properties|A user-defined key/value map to store other information.|
+
+## How to use a package
+
+Packages can efficiently use the same set of functions and IO connectors. For example, you can use the same function, source, and sink in multiple namespaces. The main steps are:
+
+1. Create a package in the package manager by providing the following information: type, tenant, namespace, package name, and version.
+
+   |Component|Description|
+   |-|-|
+   |type|Specify one of the supported package types: function, sink and source.|
+   |tenant|Specify the tenant where you want to create the package.|
+   |namespace|Specify the namespace where you want to create the package.|
+   |name|Specify the complete name of the package, using the format `<tenant>/<namespace>/<package name>`.|
+   |version|Specify the version of the package using the format `MajorVerion.MinorVersion` in numerals.|
+
+   The information you provide creates a URL for a package, in the format `<type>://<tenant>/<namespace>/<package name>/<version>`.
+
+2. Upload the elements to the package, i.e., the functions, sources, and sinks that you want to use across namespaces.
+
+3. Apply permissions to this package from various namespaces.
+
+Now, you can use the elements you defined in the package by calling this package from within the package manager. The package manager locates it by the URL. For example,
+
+```
+sink://public/default/mysql-sink@1.0
+function://my-tenant/my-ns/my-function@0.1
+source://my-tenant/my-ns/mysql-cdc-source@2.3
+```
+
+## Package management in Pulsar
+
+You can use the command line tools, REST API, or the Java client to manage your package resources in Pulsar. More specifically, you can use these tools to [upload](#upload-a-package), [download](#download-a-package), and [delete](#delete-a-package) a package, [get the metadata](#get-the-metadata-of-a-package) and [update the metadata](#update-the-metadata-of-a-package) of a package, [get the versions](#list-all-versions-of-a-package) of a package, and [get all packages of a specific type [...]
+
+To use package management service, ensure that the package management service has been enabled in your cluster by setting the following properties in `broker.conf`.
+
+:::note
+
+Package management service is not enabled by default.
+
+:::
+
+```properties
+enablePackagesManagement=true
+packagesManagementStorageProvider=org.apache.pulsar.packages.management.storage.bookkeeper.BookKeeperPackagesStorageProvider
+packagesReplicas=1
+packagesManagementLedgerRootPath=/ledgers
+```
+
+### Upload a package
+
+You can use the following commands to upload a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages upload function://public/default/example@v0.1 --path package-file --description package-description
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v3/packages/:type/:tenant/:namespace/:packageName/:version|operation/upload?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Upload a package to the package management service synchronously.
+
+```java
+void upload(PackageMetadata metadata, String packageName, String path) throws PulsarAdminException;
+```
+
+Upload a package to the package management service asynchronously.
+
+```java
+CompletableFuture<Void> uploadAsync(PackageMetadata metadata, String packageName, String path);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Download a package
+
+You can use the following commands to download a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages download function://public/default/example@v0.1 --path package-file
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/packages/:type/:tenant/:namespace/:packageName/:version|operation/download?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Download a package from the package management service synchronously.
+
+```java
+void download(String packageName, String path) throws PulsarAdminException;
+```
+
+Download a package from the package management service asynchronously.
+
+```java
+CompletableFuture<Void> downloadAsync(String packageName, String path);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete a package
+
+You can use the following commands to delete a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+The following command deletes a package of version 0.1.
+
+```shell
+bin/pulsar-admin packages delete functions://public/default/example@v0.1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v3/packages/:type/:tenant/:namespace/:packageName/:version|operation/delete?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Delete a specified package synchronously.
+
+```java
+void delete(String packageName) throws PulsarAdminException;
+```
+
+Delete a specified package asynchronously.
+
+```java
+CompletableFuture<Void> deleteAsync(String packageName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get the metadata of a package
+
+You can use the following commands to get the metadata of a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages get-metadata function://public/default/test@v1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/packages/:type/:tenant/:namespace/:packageName/:version/metadata|operation/getMeta?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Get the metadata of a package synchronously.
+
+```java
+PackageMetadata getMetadata(String packageName) throws PulsarAdminException;
+```
+
+Get the metadata of a package asynchronously.
+
+```java
+CompletableFuture<PackageMetadata> getMetadataAsync(String packageName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Update the metadata of a package
+
+You can use the following commands to update the metadata of a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages update-metadata function://public/default/example@v0.1 --description update-description
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v3/packages/:type/:tenant/:namespace/:packageName/:version/metadata|operation/updateMeta?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Update the metadata of a package synchronously.
+
+```java
+void updateMetadata(String packageName, PackageMetadata metadata) throws PulsarAdminException;
+```
+
+Update the metadata of a package asynchronously.
+
+```java
+CompletableFuture<Void> updateMetadataAsync(String packageName, PackageMetadata metadata);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List all versions of a package
+
+You can use the following commands to list all versions of a package.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages list-versions type://tenant/namespace/packageName
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/packages/:type/:tenant/:namespace/:packageName|operation/listPackageVersion?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+List all versions of a package synchronously.
+
+```java
+List<String> listPackageVersions(String packageName) throws PulsarAdminException;
+```
+
+List all versions of a package asynchronously.
+
+```java
+CompletableFuture<List<String>> listPackageVersionsAsync(String packageName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List all packages of a specific type under a namespace
+
+You can use the following commands to list all packages of a specific type under a namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="pulsar-admin">
+
+```shell
+bin/pulsar-admin packages list --type function public/default
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v3/packages/:type/:tenant/:namespace|operation/listPackages?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+List all packages of a specific type under a namespace synchronously.
+
+```java
+List<String> listPackages(String type, String namespace) throws PulsarAdminException;
+```
+
+List all packages of a specific type under a namespace asynchronously.
+
+```java
+CompletableFuture<List<String>> listPackagesAsync(String type, String namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-partitioned-topics.md b/versioned_docs/version-4.0.x/admin-api-partitioned-topics.md
new file mode 100644
index 000000000000..da99c4403022
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-partitioned-topics.md
@@ -0,0 +1,7 @@
+---
+id: admin-api-partitioned-topics
+title: Managing partitioned topics
+sidebar_label: "Partitioned topics"
+---
+
+For details of the content, refer to [manage topics](admin-api-topics.md).
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-permissions.md b/versioned_docs/version-4.0.x/admin-api-permissions.md
new file mode 100644
index 000000000000..5d1eb2e26298
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-permissions.md
@@ -0,0 +1,177 @@
+---
+id: admin-api-permissions
+title: Managing permissions
+sidebar_label: "Permissions"
+description: Learn how to manage permissions using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+ This page only shows **some frequently used operations**.
+
+ - For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+ - For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+ - For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+Pulsar allows you to grant namespace-level or topic-level permission to users.
+
+- If you grant namespace-level permission to a user, then the user can access all the topics under the namespace.
+
+- If you grant topic-level permission to a user, then the user can access only the topic.
+
+The chapters below demonstrate how to grant namespace-level permissions to users. For how to grant topic-level permissions to users, see [manage topics](admin-api-topics.md#grant-permission).
+
+## Grant permissions
+
+You can grant permissions to specific roles for lists of operations such as `produce` and `consume`.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`grant-permission`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=grant-permission) subcommand and specify a namespace, actions using the `--actions` flag, and a role using the `--role` flag:
+
+```shell
+pulsar-admin namespaces grant-permission test-tenant/namespace1 \
+    --actions produce,consume \
+    --role admin10
+```
+
+Wildcard authorization can be performed when `authorizationAllowWildcardsMatching` is set to `true` in `broker.conf`.
+
+**Example**
+
+```shell
+pulsar-admin namespaces grant-permission test-tenant/namespace1 \
+      --actions produce,consume \
+      --role 'my.role.*'
+```
+
+Then, roles `my.role.1`, `my.role.2`, `my.role.foo`, `my.role.bar`, etc. can produce and consume.
+
+```shell
+pulsar-admin namespaces grant-permission test-tenant/namespace1 \
+      --actions produce,consume \
+      --role '*.role.my'
+```
+
+Then, roles `1.role.my`, `2.role.my`, `foo.role.my`, `bar.role.my`, etc. can produce and consume.
+
+:::note
+
+A wildcard matching works at **the beginning or end of the role name only**.
+
+:::
+
+**Example**
+
+```shell
+pulsar-admin namespaces grant-permission test-tenant/namespace1 \
+      --actions produce,consume \
+      --role 'my.*.role'
+```
+
+In this case, only the role `my.*.role` has permissions.
+Roles `my.1.role`, `my.2.role`, `my.foo.role`, `my.bar.role`, etc. **cannot** produce and consume.
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/permissions/:role|operation/grantPermissionOnNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().grantPermissionOnNamespace(namespace, role, getAuthActions(actions));
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Get permissions
+
+You can see which permissions have been granted to which roles in a namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`permissions`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=grant-permission) subcommand and specify a namespace:
+
+```shell
+pulsar-admin namespaces permissions test-tenant/namespace1
+```
+
+Example output:
+
+```
+my.role.*    [produce, consume]
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/permissions|operation/getPermissions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().getPermissions(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Revoke permissions
+
+You can revoke permissions from specific roles, which means that those roles will no longer have access to the specified namespace.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`revoke-permission`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=revoke-permission) subcommand and specify a namespace and a role using the `--role` flag:
+
+```shell
+pulsar-admin namespaces revoke-permission test-tenant/namespace1 \
+      --role admin10
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/permissions/:role|operation/revokePermissionsOnNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.namespaces().revokePermissionsOnNamespace(namespace, role);
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-persistent-topics.md b/versioned_docs/version-4.0.x/admin-api-persistent-topics.md
new file mode 100644
index 000000000000..206304005dae
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-persistent-topics.md
@@ -0,0 +1,7 @@
+---
+id: admin-api-persistent-topics
+title: Managing persistent topics
+sidebar_label: "Persistent topics"
+---
+
+For details of the content, refer to [manage topics](admin-api-topics.md).
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/admin-api-schemas.md b/versioned_docs/version-4.0.x/admin-api-schemas.md
new file mode 100644
index 000000000000..fb8914980ff9
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-schemas.md
@@ -0,0 +1,678 @@
+---
+id: admin-api-schemas
+title: Manage Schemas
+sidebar_label: "Schemas"
+description: Learn how to manage schemas using Pulsar CLI and admin APIs.
+---
+
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+:::tip
+
+This page only shows **some frequently used operations**.
+
+- For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+- For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+- For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+## Manage schema
+
+### Upload a schema
+
+To upload (register) a new schema for a topic, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `upload` subcommand.
+
+```bash
+pulsar-admin schemas upload --filename <schema-definition-file> <topic-name>
+```
+
+The `schema-definition-file` is in JSON format.
+
+```json
+{
+    "type": "<schema-type>",
+    "schema": "<an-utf8-encoded-string-of-schema-definition-data>",
+    "properties": {} // the properties associated with the schema
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `POST` request to the endpoint documented here: {@inject: endpoint|POST|/admin/v2/schemas/:tenant/:namespace/:topic/schema|operation/SchemasResource_postSchema?version=@pulsar:version_number@}
+
+Below is an example with CURL with a payload stored on the `schema.json` file, Pulsar broker running on `localhost` and the topic `my-tenant/my-ns/my-topic`:
+
+```bash
+curl -X POST -H 'Content-Type: application/json' -d @schema.json http://localhost:8080/admin/v2/schemas/my-tenant/my-ns/my-topic/schema
+```
+
+The post payload is in JSON format.
+
+```json
+{
+    "type": "<schema-type>",
+    "schema": "<an-utf8-encoded-string-of-schema-definition-data>",
+    "properties": {} // the properties associated with the schema
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+The method on `PulsarAdmin` client is:
+```java
+void createSchema(String topic, PostSchemaPayload schemaPayload)
+```
+
+Here is an example of `PostSchemaPayload`:
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+PostSchemaPayload payload = new PostSchemaPayload();
+payload.setType("INT8");
+payload.setSchema("");
+
+admin.createSchema("my-tenant/my-ns/my-topic", payload);
+```
+
+If the schema is a **primitive** schema, the `schema` field must be blank.
+If the schema is a **struct** schema, this field must be a JSON string of the Avro schema definition.
+
+</TabItem>
+</Tabs>
+````
+
+The payload includes the following fields:
+
+| Field        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                        |
+|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `type`       | <li>Allowed values for primitive-type schemas are listed on the following page: [Primitive types](schema-understand.md#primitive-type)</li><li>Allowed values for struct-type schemas are **AVRO**, **PROTOBUF**, **PROTOBUF_NATIVE** and **JSON**.</li>                                                                                                                                                                                           |
+| `schema`     | The schema definition data, which is encoded in UTF 8 charset. <li>If the schema type is **AVRO**, **PROTOBUF** or **JSON** schema, this field should be an <a href="https://avro.apache.org/docs/1.11.1/specification/" target="blank">Avro schema definition</a> in JSON format.</li><li>If the schema type is **PROTOBUF_NATIVE** schema, this field should contain a Protobuf descriptor. </li><li>Otherwise, this field should be blank.</li> |
+| `properties` | The additional properties associated with the schema.                                                                                                                                                                                                                                                                                                                                                                                              |
+
+The following is an example for a JSON schema.
+
+**Example**
+
+```json
+{
+    "type": "JSON",
+    "schema": "{\"type\":\"record\",\"name\":\"User\",\"namespace\":\"com.foo\",\"fields\":[{\"name\":\"file1\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"file2\",\"type\":[\"null\",\"string\"],\"default\":null},{\"name\":\"file3\",\"type\":[\"string\",\"null\"],\"default\":\"dfdf\"}]}",
+    "properties": {}
+}
+```
+
+### Get the latest schema
+
+To get the latest schema for a topic, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `get` subcommand.
+
+```bash
+pulsar-admin schemas get <topic-name>
+```
+
+Example output:
+
+```json
+{
+    "version": 0,
+    "type": "String",
+    "timestamp": 0,
+    "data": "string",
+    "properties": {
+        "property1": "string",
+        "property2": "string"
+    }
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `GET` request to this endpoint: {@inject: endpoint|GET|/admin/v2/schemas/:tenant/:namespace/:topic/schema|operation/SchemasResource_getSchema?version=@pulsar:version_number@}
+
+Here is an example of a response, which is returned in JSON format.
+
+```json
+{
+    "version": "<the-version-number-of-the-schema>",
+    "type": "<the-schema-type>",
+    "timestamp": "<the-creation-timestamp-of-the-version-of-the-schema>",
+    "data": "<an-utf8-encoded-string-of-schema-definition-data>",
+    "properties": {} // the properties associated with the schema
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+SchemaInfo createSchema(String topic)
+```
+
+Here is an example of `SchemaInfo`:
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+SchemaInfo si = admin.getSchema("my-tenant/my-ns/my-topic");
+```
+
+</TabItem>
+</Tabs>
+````
+
+### Get a specific schema
+
+To get a specific version of a schema, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `get` subcommand.
+
+```bash
+pulsar-admin schemas get <topic-name> --version <version>
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `GET` request to a schema endpoint: {@inject: endpoint|GET|/admin/v2/schemas/:tenant/:namespace/:topic/schema/:version|operation/SchemasResource_getSchema?version=@pulsar:version_number@}
+
+Here is an example of a response, which is returned in JSON format.
+
+```json
+{
+    "version": "<the-version-number-of-the-schema>",
+    "type": "<the-schema-type>",
+    "timestamp": "<the-creation-timestamp-of-the-version-of-the-schema>",
+    "data": "<an-utf8-encoded-string-of-schema-definition-data>",
+    "properties": {} // the properties associated with the schema
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+SchemaInfo createSchema(String topic, long version)
+```
+
+Here is an example of `SchemaInfo`:
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+SchemaInfo si = admin.getSchema("my-tenant/my-ns/my-topic", 1L);
+```
+
+</TabItem>
+</Tabs>
+````
+
+### Extract a schema
+
+To extract (provide) a schema via a topic, use the following method.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `extract` subcommand.
+
+```bash
+pulsar-admin schemas extract --classname <class-name> --jar <absolute-jar-path> --type <type-name>
+```
+
+</TabItem>
+</Tabs>
+````
+
+### Delete a schema
+
+:::note
+
+In any case, the `delete` action deletes **all versions** of a schema registered for a topic.
+
+:::
+
+To delete a schema for a topic, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `delete` subcommand.
+
+```bash
+pulsar-admin schemas delete <topic-name>
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `DELETE` request to a schema endpoint: {@inject: endpoint|DELETE|/admin/v2/schemas/:tenant/:namespace/:topic/schema|operation/SchemasResource_deleteSchema?version=@pulsar:version_number@}
+
+Here is an example of a response returned in JSON format.
+
+```json
+{
+    "version": "<the-latest-version-number-of-the-schema>",
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+void deleteSchema(String topic)
+```
+
+Here is an example of deleting a schema.
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+admin.deleteSchema("my-tenant/my-ns/my-topic");
+```
+
+</TabItem>
+</Tabs>
+````
+
+## Manage schema AutoUpdate
+
+### Enable schema AutoUpdate
+
+To enable/enforce schema auto-update at the namespace level, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `set-is-allow-auto-update-schema` subcommand.
+
+```bash
+bin/pulsar-admin namespaces set-is-allow-auto-update-schema --enable tenant/namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `POST` request to a namespace endpoint: {@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/isAllowAutoUpdateSchema|operation/Namespaces_setIsAllowAutoUpdateSchema?version=@pulsar:version_number@}
+
+The post payload is in JSON format.
+
+```json
+{
+โ€œisAllowAutoUpdateSchemaโ€: โ€œtrueโ€
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+Here is an example to enable schema auto-update for a tenant/namespace.
+
+```java
+admin.namespaces().setIsAllowAutoUpdateSchema("my-namspace", true);
+```
+
+</TabItem>
+</Tabs>
+````
+
+### Disable schema AutoUpdate
+
+:::note
+
+When schema auto-update is disabled, you can only [register a new schema](#upload-a-schema).
+
+:::
+
+To disable schema auto-update at the **namespace** level, you can use one of the following commands.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `set-is-allow-auto-update-schema` subcommand.
+
+```bash
+bin/pulsar-admin namespaces set-is-allow-auto-update-schema --disable tenant/namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `POST` request to a namespace endpoint: {@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/isAllowAutoUpdateSchema|operation/Namespaces_setIsAllowAutoUpdateSchema?version=@pulsar:version_number@}
+
+The post payload is in JSON format.
+
+```json
+{
+โ€œisAllowAutoUpdateSchemaโ€: โ€œfalseโ€
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+Here is an example to enable schema auto-unpdate of a tenant/namespace.
+
+```java
+admin.namespaces().setIsAllowAutoUpdateSchema("my-namspace", false);
+```
+
+</TabItem>
+</Tabs>
+````
+
+## Manage schema validation enforcement
+
+### Enable schema validation enforcement
+
+To enforce schema validation enforcement at the **cluster** level, you can configure `isSchemaValidationEnforced` to `true` in the `conf/broker.conf` file.
+
+To enable schema validation enforcement at the **namespace** level, you can use one of the following commands.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `set-schema-validation-enforce` subcommand.
+
+```bash
+bin/pulsar-admin namespaces set-schema-validation-enforce --enable tenant/namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `POST` request to a namespace endpoint: {@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/schemaValidationEnforced|operation/Namespaces_setSchemaValidationEnforced?version=@pulsar:version_number@}
+
+The post payload is in JSON format.
+
+```json
+{
+โ€œschemaValidationEnforcedโ€: โ€œtrueโ€
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+Here is an example to enable schema validation enforcement for a tenant/namespace.
+
+```java
+admin.namespaces().setSchemaValidationEnforced("my-namspace", true);
+```
+
+</TabItem>
+</Tabs>
+````
+
+### Disable schema validation enforcement
+
+To disable schema validation enforcement at the **namespace** level, you can use one of the following commands.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the `set-schema-validation-enforce` subcommand.
+
+```bash
+bin/pulsar-admin namespaces set-schema-validation-enforce --disable tenant/namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `POST` request to a namespace endpoint: {@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/schemaValidationEnforced|operation/Namespaces_setSchemaValidationEnforced?version=@pulsar:version_number@}
+
+The post payload is in JSON format.
+
+```json
+{
+โ€œschemaValidationEnforcedโ€: โ€œfalseโ€
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+Here is an example to enable schema validation enforcement for a tenant/namespace.
+
+```java
+admin.namespaces().setSchemaValidationEnforced("my-namspace", false);
+```
+
+</TabItem>
+</Tabs>
+````
+
+## Manage schema compatibility strategy
+
+The [schema compatibility check strategy](schema-understand.md#schema-compatibility-check-strategy) configured at different levels has priority: topic level > namespace level > cluster level. In other words:
+  * If you set the strategy at both topic and namespace levels, the topic-level strategy is used.
+  * If you set the strategy at both namespace and cluster levels, the namespace-level strategy is used.
+
+### Set schema compatibility strategy
+
+#### Set topic-level schema compatibility strategy
+
+To set a schema compatibility check strategy at the topic level, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the [`pulsar-admin topicPolicies set-schema-compatibility-strategy`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topicPolicies?id=set-schema-compatibility-strategy) command.
+
+```shell
+pulsar-admin topicPolicies set-schema-compatibility-strategy <strategy> <topicName>
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `PUT` request to this endpoint: {@inject: endpoint|PUT|/admin/v2/topics/:tenant/:namespace/:topic|operation/PersistentTopics_setSchemaCompatibilityStrategy?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+void setSchemaCompatibilityStrategy(String topic, SchemaCompatibilityStrategy strategy)
+```
+
+Here is an example of setting a schema compatibility check strategy at the topic level.
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+admin.topicPolicies().setSchemaCompatibilityStrategy("my-tenant/my-ns/my-topic", SchemaCompatibilityStrategy.ALWAYS_INCOMPATIBLE);
+```
+
+</TabItem>
+</Tabs>
+````
+
+#### Set namespace-level schema compatibility strategy
+
+To set schema compatibility check strategy at the namespace level, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the [`pulsar-admin namespaces set-schema-compatibility-strategy`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=set-schema-compatibility-strategy) command.
+
+```shell
+pulsar-admin namespaces set-schema-compatibility-strategy options
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `PUT` request to this endpoint: {@inject: endpoint|PUT|/admin/v2/namespaces/:tenant/:namespace/schemaCompatibilityStrategy|operation/Namespaces_setSchemaCompatibilityStrategy?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Use the [`setSchemaCompatibilityStrategy`](/api/admin/) method.
+
+```java
+admin.namespaces().setSchemaCompatibilityStrategy("test", SchemaCompatibilityStrategy.FULL);
+```
+
+</TabItem>
+</Tabs>
+````
+
+#### Set cluster-level schema compatibility strategy
+
+To set schema compatibility check strategy at the **cluster** level, set `schemaCompatibilityStrategy` in the `conf/broker.conf` file.
+
+The following is an example:
+
+```conf
+schemaCompatibilityStrategy=ALWAYS_INCOMPATIBLE
+```
+
+### Get schema compatibility strategy
+
+#### Get topic-level schema compatibility strategy
+
+To get the topic-level schema compatibility check strategy, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the [`pulsar-admin topicPolicies get-schema-compatibility-strategy`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topicPolicies?id=get-schema-compatibility-strategy) command.
+
+```shell
+pulsar-admin topicPolicies get-schema-compatibility-strategy <topicName>
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `GET` request to this endpoint: {@inject: endpoint|GET|/admin/v2/topics/:tenant/:namespace/:topic/schemaCompatibilityStrategy|operation/PersistentTopics_getSchemaCompatibilityStrategy?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+SchemaCompatibilityStrategy getSchemaCompatibilityStrategy(String topic, boolean applied)
+```
+
+Here is an example of getting the topic-level schema compatibility check strategy.
+
+```java
+PulsarAdmin admin = โ€ฆ;
+
+// get the current applied schema compatibility strategy
+admin.topicPolicies().getSchemaCompatibilityStrategy("my-tenant/my-ns/my-topic", true);
+
+// only get the schema compatibility strategy from topic policies
+admin.topicPolicies().getSchemaCompatibilityStrategy("my-tenant/my-ns/my-topic", false);
+```
+
+</TabItem>
+</Tabs>
+````
+
+#### Get namespace-level schema compatibility strategy
+
+You can get schema compatibility check strategy at namespace level using one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="Admin CLI"
+  values={[{"label":"Admin CLI","value":"Admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="Admin CLI">
+
+Use the [`pulsar-admin namespaces get-schema-compatibility-strategy`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/namespaces?id=get-schema-compatibility-strategy) command.
+
+```shell
+pulsar-admin namespaces get-schema-compatibility-strategy options
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+Send a `GET` request to this endpoint: {@inject: endpoint|GET|/admin/v2/namespaces/:tenant/:namespace/schemaCompatibilityStrategy|operation/Namespaces_getSchemaCompatibilityStrategy?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+Use the [`getSchemaCompatibilityStrategy`](/api/admin/) method.
+
+```java
+admin.namespaces().getSchemaCompatibilityStrategy("test", SchemaCompatibilityStrategy.FULL);
+```
+
+</TabItem>
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-tenants.md b/versioned_docs/version-4.0.x/admin-api-tenants.md
new file mode 100644
index 000000000000..7d61305fb922
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-tenants.md
@@ -0,0 +1,234 @@
+---
+id: admin-api-tenants
+title: Managing Tenants
+sidebar_label: "Tenants"
+description: Learn how to manage tenants using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+ This page only shows **some frequently used operations**.
+
+ - For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+ - For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+ - For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+Tenants, like namespaces, can be managed using the [admin API](admin-api-overview.md). There are currently two configurable aspects of tenants:
+
+* Admin roles
+* Allowed clusters
+
+## Tenant resources
+
+### List
+
+You can list all of the tenants associated with an [instance](reference-terminology.md#instance).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`list`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/tenants?id=list) subcommand.
+
+```shell
+pulsar-admin tenants list
+```
+
+Output:
+
+```
+my-tenant-1
+my-tenant-2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/tenants|operation/getTenants?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.tenants().getTenants();
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Create
+
+You can create a new tenant.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`create`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/tenants?id=create) subcommand:
+
+```shell
+pulsar-admin tenants create my-tenant
+```
+
+When creating a tenant, you can optionally assign admin roles using the `-r`/`--admin-roles`
+flag, and clusters using the `-c`/`--allowed-clusters` flag. You can specify multiple values
+as a comma-separated list. Here are some examples:
+
+```shell
+pulsar-admin tenants create my-tenant \
+    --admin-roles role1,role2,role3 \
+    --allowed-clusters cluster1
+```
+
+```shell
+pulsar-admin tenants create my-tenant \
+    -r role1 \
+    -c cluster1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/tenants/:tenant|operation/createTenant?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.tenants().createTenant(tenantName, tenantInfo);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get configuration
+
+You can fetch the [configuration](reference-configuration.md) for an existing tenant at any time.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`get`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/tenants?id=get) subcommand and specify the name of the tenant. Here's an example:
+
+```shell
+pulsar-admin tenants get my-tenant
+```
+
+```json
+{
+  "adminRoles": [
+    "admin1",
+    "admin2"
+  ],
+  "allowedClusters": [
+    "cl1",
+    "cl2"
+  ]
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/tenants/:tenant|operation/getTenant?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.tenants().getTenantInfo(tenantName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete
+
+Tenants can be deleted from a Pulsar [instance](reference-terminology.md#instance).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`delete`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/tenants?id=delete) subcommand and specify the name of the tenant.
+
+```shell
+pulsar-admin tenants delete my-tenant
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/tenants/:tenant|operation/deleteTenant?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.Tenants().deleteTenant(tenantName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Update
+
+You can update a tenant's configuration.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`update`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/tenants?id=update) subcommand.
+
+```shell
+pulsar-admin tenants update my-tenant \
+    --admin-roles role1,role2 \
+    --allowed-clusters cluster1,cluster2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/tenants/:tenant|operation/updateTenant?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.tenants().updateTenant(tenantName, tenantInfo);
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-tools.md b/versioned_docs/version-4.0.x/admin-api-tools.md
new file mode 100644
index 000000000000..d3cec7a83089
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-tools.md
@@ -0,0 +1,49 @@
+---
+id: admin-api-tools
+title: Pulsar admin interfaces - Tools
+sidebar_label: "Tools"
+description: Get a comprehensive understanding of concepts and difference of Pulsar admin CLI and Pulsar admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+You can manage Pulsar entities through the Pulsar admin layer via one of the following tools:
+
+- Pulsar admin APIs
+
+  - [Java admin API](/api/admin/): Itโ€™s a programmable interface written in Java.
+
+  - Go admin API (coming soon)
+
+  - [REST API](pathname:///admin-rest-api/?version=@pulsar:version_number@): HTTP calls, which are made against the admin APIs provided by brokers. In addition, both the Java admin API and pulsar-admin CLI use the REST API.
+
+- [pulsar-admin CLI](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/): Itโ€™s a command-line tool and is available in the bin folder of your Pulsar installation.
+
+![Tools of Pulsar admin layer](/assets/admin-api-tools.svg)
+
+Here are the explanations and comparisons of these tools.
+
+Category|Tools|When to use|Considerations
+|---|---|---|---
+Pulsar admin APIs|[Java admin API](/api/admin/)| - If you want to implement your own admin interface client using Java and manage clusters. <br/><br/> - If you want to manage resources programmatically rather than relying on external tools inside of unit tests.<br/><br/> - If you want to use admin APIs in Java applications.| <br/><br/> - This method is only available in Java. <br/><br/> - It needs more development work if you want to use it to build applications.
+Pulsar admin APIs | [REST API](pathname:///admin-rest-api/?version=@pulsar:version_number@)|- If you want to implement your own admin interface client using other languages and manage clusters using scripts.| - This method is the most complicated. <br/><br/> - It needs more development work if you want to use it to build applications.
+Pulsar admin CLI| [pulsar-admin CLI](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/) | - If you want to get started with Pulsar admin APIs with minimal effort (e.g., no need to prepare an extra environment). <br/><br/> - If you want to perform common administrative tasks.| - This method is the most easy-to-use. <br/><br/> - Itโ€™s challenging to use this method to build applications.<br/><br/> - It takes a little more time because JVM starts slowly.
+
+### Related topics
+
+- To understand the basics, see [Pulsar admin API - Overview](admin-api-overview.md)
+
+- To learn usage scenarios, see [Pulsar admin API - Use cases](admin-api-use-cases.md).
+
+- To learn common administrative tasks, see [Pulsar admin API - Features](admin-api-features.md).
+
+- To get up quickly, see [Pulsar admin API - Get started](admin-get-started.md).
+
+- To check the detailed usage, see the API references below.
+
+  - [Java admin API](/api/admin/)
+
+  - [REST API](reference-rest-api-overview.md)
diff --git a/versioned_docs/version-4.0.x/admin-api-topics.md b/versioned_docs/version-4.0.x/admin-api-topics.md
new file mode 100644
index 000000000000..5f7866553ffa
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-topics.md
@@ -0,0 +1,2064 @@
+---
+id: admin-api-topics
+title: Manage topics
+sidebar_label: "Topics"
+description: Learn how to manage topics using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+This page only shows **some frequently used operations**.
+
+- For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+- For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+- For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+Pulsar has persistent and non-persistent topics. A persistent topic is a logical endpoint for publishing and consuming messages. The topic name structure for persistent topics is:
+
+```shell
+persistent://tenant/namespace/topic
+```
+
+Non-persistent topics are used in applications that only consume real-time published messages and do not need persistent guarantees. In this way, it reduces message-publish latency by removing overhead of persisting messages. The topic name structure for non-persistent topics is:
+
+```shell
+non-persistent://tenant/namespace/topic
+```
+
+:::note
+
+Topic naming: due to backward compatibility, some special characters, e.g., "/," are allowed as a part of the topic name. But it's recommended not to use special characters as a part of the topic name.
+
+:::
+
+## Manage topic resources
+Whether it is a persistent or non-persistent topic, you can obtain the topic resources through `pulsar-admin` tool, REST API and Java.
+
+:::note
+
+In REST API, `:schema` stands for persistent or non-persistent. `:tenant`, `:namespace`, `:x` are variables, replace them with the real tenant, namespace, and `x` names when using them.
+Take {@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace|operation/getList?version=@pulsar:version_number@} as an example, to get the list of persistent topics in REST API, use `https://pulsar.apache.org/admin/v2/persistent/my-tenant/my-namespace`. To get the list of non-persistent topics in REST API, use `https://pulsar.apache.org/admin/v2/non-persistent/my-tenant/my-namespace`.
+
+:::
+
+### List of topics
+
+You can get the list of topics under a given namespace in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics list my-tenant/my-namespace
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace|operation/getList?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String namespace = "my-tenant/my-namespace";
+admin.topics().getList(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Grant permission
+
+You can grant permissions on a client role to perform specific actions on a given topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics grant-permission \
+    --actions produce,consume \
+    --role application1 \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:namespace/:topic/permissions/:role|operation/grantPermissionsOnTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String role = "test-role";
+Set<AuthAction> actions  = Sets.newHashSet(AuthAction.produce, AuthAction.consume);
+admin.topics().grantPermission(topic, role, actions);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get permission
+
+You can fetch permission in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics permissions persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+application1    [consume, produce]
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/permissions|operation/getPermissionsOnTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getPermissions(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Revoke permission
+
+You can revoke permissions granted on a client role in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics revoke-permission \
+    --role application1 \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/:schema/:tenant/:namespace/:topic/permissions/:role|operation/revokePermissionsOnTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String role = "test-role";
+admin.topics().revokePermissions(topic, role);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete topic
+
+You can delete a topic in the following ways. You cannot delete a topic if any active subscription or producer is connected to the topic.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics delete persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/:schema/:tenant/:namespace/:topic|operation/deleteTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().delete(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Unload topic
+
+You can unload a topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics unload persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/:schema/:tenant/:namespace/:topic/unload|operation/unloadTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().unload(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get stats
+
+For the detailed statistics of a topic, see [Pulsar statistics](administration-stats.md#topic-stats).
+
+The following is an example of a topic status.
+
+```json
+{
+  "msgRateIn" : 0.0,
+  "msgThroughputIn" : 0.0,
+  "msgRateOut" : 0.0,
+  "msgThroughputOut" : 0.0,
+  "bytesInCounter" : 504,
+  "msgInCounter" : 9,
+  "bytesOutCounter" : 2296,
+  "msgOutCounter" : 41,
+  "averageMsgSize" : 0.0,
+  "msgChunkPublished" : false,
+  "storageSize" : 504,
+  "backlogSize" : 0,
+  "filteredEntriesCount" : 100,
+  "earliestMsgPublishTimeInBacklogs": 0,
+  "offloadedStorageSize" : 0,
+  "publishers" : [ {
+    "accessMode" : "Shared",
+    "msgRateIn" : 0.0,
+    "msgThroughputIn" : 0.0,
+    "averageMsgSize" : 0.0,
+    "chunkedMessageRate" : 0.0,
+    "producerId" : 0,
+    "metadata" : { },
+    "address" : "/127.0.0.1:65402",
+    "connectedSince" : "2021-06-09T17:22:55.913+08:00",
+    "clientVersion" : "2.9.0-SNAPSHOT",
+    "producerName" : "standalone-1-0"
+  } ],
+  "waitingPublishers" : 0,
+  "subscriptions" : {
+    "sub-demo" : {
+      "msgRateOut" : 0.0,
+      "msgThroughputOut" : 0.0,
+      "bytesOutCounter" : 2296,
+      "msgOutCounter" : 41,
+      "msgRateRedeliver" : 0.0,
+      "chunkedMessageRate" : 0,
+      "msgBacklog" : 0,
+      "backlogSize" : 0,
+      "earliestMsgPublishTimeInBacklog": 0,
+      "msgBacklogNoDelayed" : 0,
+      "blockedSubscriptionOnUnackedMsgs" : false,
+      "msgDelayed" : 0,
+      "unackedMessages" : 0,
+      "type" : "Exclusive",
+      "activeConsumerName" : "20b81",
+      "msgRateExpired" : 0.0,
+      "totalMsgExpired" : 0,
+      "lastExpireTimestamp" : 0,
+      "lastConsumedFlowTimestamp" : 1623230565356,
+      "lastConsumedTimestamp" : 1623230583946,
+      "lastAckedTimestamp" : 1623230584033,
+      "lastMarkDeleteAdvancedTimestamp" : 1623230584033,
+      "filterProcessedMsgCount": 100,
+      "filterAcceptedMsgCount": 100,
+      "filterRejectedMsgCount": 0,
+      "filterRescheduledMsgCount": 0,
+      "consumers" : [ {
+        "msgRateOut" : 0.0,
+        "msgThroughputOut" : 0.0,
+        "bytesOutCounter" : 2296,
+        "msgOutCounter" : 41,
+        "msgRateRedeliver" : 0.0,
+        "chunkedMessageRate" : 0.0,
+        "consumerName" : "20b81",
+        "availablePermits" : 959,
+        "unackedMessages" : 0,
+        "avgMessagesPerEntry" : 314,
+        "blockedConsumerOnUnackedMsgs" : false,
+        "lastAckedTimestamp" : 1623230584033,
+        "lastConsumedTimestamp" : 1623230583946,
+        "metadata" : { },
+        "address" : "/127.0.0.1:65172",
+        "connectedSince" : "2021-06-09T17:22:45.353+08:00",
+        "clientVersion" : "2.9.0-SNAPSHOT"
+      } ],
+      "allowOutOfOrderDelivery": false,
+      "consumersAfterMarkDeletePosition" : { },
+      "nonContiguousDeletedMessagesRanges" : 0,
+      "nonContiguousDeletedMessagesRangesSerializedSize" : 0,
+      "durable" : true,
+      "replicated" : false
+    }
+  },
+  "replication" : { },
+  "deduplicationStatus" : "Disabled",
+  "nonContiguousDeletedMessagesRanges" : 0,
+  "nonContiguousDeletedMessagesRangesSerializedSize" : 0,
+  "ownerBroker" : "localhost:8080"
+}
+```
+
+To get the status of a topic, you can use the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics stats persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/stats|operation/getStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getStats(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get internal stats
+
+For the detailed internal statistics inside a topic, see [Pulsar statistics](administration-stats.md#topic-internal-stats).
+
+The following is an example of the internal statistics of a topic.
+
+```json
+{
+    "entriesAddedCounter":0,
+    "numberOfEntries":0,
+    "totalSize":0,
+    "currentLedgerEntries":0,
+    "currentLedgerSize":0,
+    "lastLedgerCreatedTimestamp":"2021-01-22T21:12:14.868+08:00",
+    "lastLedgerCreationFailureTimestamp":null,
+    "waitingCursorsCount":0,
+    "pendingAddEntriesCount":0,
+    "lastConfirmedEntry":"3:-1",
+    "state":"LedgerOpened",
+    "ledgers":[
+        {
+            "ledgerId":3,
+            "entries":0,
+            "size":0,
+            "offloaded":false,
+            "metadata":null
+        }
+    ],
+    "cursors":{
+        "test":{
+            "markDeletePosition":"3:-1",
+            "readPosition":"3:-1",
+            "waitingReadOp":false,
+            "pendingReadOps":0,
+            "messagesConsumedCounter":0,
+            "cursorLedger":4,
+            "cursorLedgerLastEntry":1,
+            "individuallyDeletedMessages":"[]",
+            "lastLedgerSwitchTimestamp":"2021-01-22T21:12:14.966+08:00",
+            "state":"Open",
+            "numberOfEntriesSinceFirstNotAckedMessage":0,
+            "totalNonContiguousDeletedMessagesRange":0,
+            "properties":{
+
+            }
+        }
+    },
+    "schemaLedgers":[
+        {
+            "ledgerId":1,
+            "entries":11,
+            "size":10,
+            "offloaded":false,
+            "metadata":null
+        }
+    ],
+    "compactedLedger":{
+        "ledgerId":-1,
+        "entries":-1,
+        "size":-1,
+        "offloaded":false,
+        "metadata":null
+    }
+}
+```
+
+To get the internal status of a topic, you can use the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics stats-internal persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/internalStats|operation/getInternalStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getInternalStats(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Peek messages
+
+You can peek a number of messages for a specific subscription of a given topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics peek-messages \
+    --count 10 --subscription my-subscription \
+    persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+Message ID: 77:2
+Publish time: 1668674963028
+Event time: 0
+         +-------------------------------------------------+
+         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
++--------+-------------------------------------------------+----------------+
+|00000000| 68 65 6c 6c 6f 2d 31                            |hello-1         |
++--------+-------------------------------------------------+----------------+
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/subscription/:subName/position/:messagePosition|operation/peekNthMessage?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subName = "my-subscription";
+int numMessages = 1;
+admin.topics().peekMessages(topic, subName, numMessages);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get message by ID
+
+You can fetch the message with the given ledger ID and entry ID in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-message-by-id \
+    -l 10 -e 0 persistent://public/default/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/ledger/:ledgerId/entry/:entryId|operation/getMessageById?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+long ledgerId = 10;
+long entryId = 10;
+admin.topics().getMessageById(topic, ledgerId, entryId);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Examine messages
+
+You can examine a specific message on a topic by position relative to the earliest or the latest message.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics examine-messages \
+    -i latest -m 1 persistent://public/default/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/examinemessage?initialPosition=:initialPosition&messagePosition=:messagePosition|operation/examineMessage?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().examineMessage(topic, "latest", 1);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get message ID
+
+You can get message ID published at or just after the given datetime.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-message-id \
+    persistent://public/default/my-topic \
+    -d 2021-06-28T19:01:17Z
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/messageid/:timestamp|operation/getMessageIdByTimestamp?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+long timestamp = System.currentTimeMillis()
+admin.topics().getMessageIdByTimestamp(topic, timestamp);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+### Skip messages
+
+You can skip a number of messages for a specific subscription of a given topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics skip \
+    --count 10 --subscription my-subscription \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:namespace/:topic/subscription/:subName/skip/:numMessages|operation/skipMessages?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subName = "my-subscription";
+int numMessages = 1;
+admin.topics().skipMessages(topic, subName, numMessages);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Skip all messages
+
+You can skip all the old messages for a specific subscription of a given topic.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics clear-backlog \
+    --subscription my-subscription \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:namespace/:topic/subscription/:subName/skip_all|operation/skipAllMessages?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subName = "my-subscription";
+admin.topics().skipAllMessages(topic, subName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Reset cursor
+
+You can reset a subscription cursor position back to the position which is recorded X minutes before. It essentially calculates the time and position of the cursor at X minutes before and resets it at that position. You can reset the cursor in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics reset-cursor \
+    --subscription my-subscription --time 10 \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:namespace/:topic/subscription/:subName/resetcursor/:timestamp|operation/resetCursor?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subName = "my-subscription";
+long timestamp = 2342343L;
+admin.topics().resetCursor(topic, subName, timestamp);
+
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Look up topic's owner broker
+
+You can locate the owner broker of the given topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics lookup persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+"pulsar://broker1.org.com:4480"
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/lookup/v2/topic/:topic-domain/:tenant/:namespace/:topic|operation/lookupTopicAsync?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.lookups().lookupDestination(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Look up partitioned topic's owner broker
+
+You can locate the owner broker of the given partitioned topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics partitioned-lookup persistent://test-tenant/ns1/my-topic
+```
+
+Example output:
+
+```
+"persistent://test-tenant/ns1/my-topic-partition-0   pulsar://localhost:6650"
+"persistent://test-tenant/ns1/my-topic-partition-1   pulsar://localhost:6650"
+"persistent://test-tenant/ns1/my-topic-partition-2   pulsar://localhost:6650"
+"persistent://test-tenant/ns1/my-topic-partition-3   pulsar://localhost:6650"
+```
+
+Lookup the partitioned topics sorted by broker URL
+
+```shell
+pulsar-admin topics partitioned-lookup \
+    persistent://test-tenant/ns1/my-topic --sort-by-broker
+```
+
+Example output:
+
+```
+pulsar://localhost:6650   [persistent://test-tenant/ns1/my-topic-partition-0, persistent://test-tenant/ns1/my-topic-partition-1, persistent://test-tenant/ns1/my-topic-partition-2, persistent://test-tenant/ns1/my-topic-partition-3]
+```
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.lookups().lookupPartitionedTopic(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get bundle
+
+You can get the range of the bundle that the given topic belongs to in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics bundle-range persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+"0x00000000_0xffffffff"
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/lookup/v2/topic/:topic_domain/:tenant/:namespace/:topic/bundle|operation/getNamespaceBundle?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.lookups().getBundleRange(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get subscriptions
+
+You can check all subscription names for a given topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics subscriptions persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+my-subscription
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/subscriptions|operation/getSubscriptions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getSubscriptions(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Last Message Id
+
+You can get the last committed message ID for a persistent topic. It is available since 2.3.0 release.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics last-message-id topic-name
+```
+
+Example output:
+
+```json
+{
+  "ledgerId" : 97,
+  "entryId" : 9,
+  "partitionIndex" : -1
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|Get|/admin/v2/:schema/:tenant/:namespace/:topic/lastMessageId|operation/getLastMessageId?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getLastMessage(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get backlog size
+
+You can get the backlog size of a single partition topic or a non-partitioned topic with a given message ID (in bytes).
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-backlog-size \
+  -m 1:1 \
+  persistent://test-tenant/ns1/tp1-partition-0
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/:schema/:tenant/:namespace/:topic/backlogSize|operation/getBacklogSizeByMessageId?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+MessageId messageId = MessageId.earliest;
+admin.topics().getBacklogSizeByMessageId(topic, messageId);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+### Configure deduplication snapshot interval
+
+#### Get deduplication snapshot interval
+
+To get the topic-level deduplication snapshot interval, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-deduplication-snapshot-interval my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/topics/:tenant/:namespace/:topic/deduplicationSnapshotInterval|operation/getDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getDeduplicationSnapshotInterval(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Set deduplication snapshot interval
+
+To set the topic-level deduplication snapshot interval, use one of the following methods.
+
+> **Prerequisite** `brokerDeduplicationEnabled` must be set to `true`.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics set-deduplication-snapshot-interval my-topic -i 1000
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/topics/:tenant/:namespace/:topic/deduplicationSnapshotInterval|operation/setDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+```json
+{
+  "interval": 1000
+}
+```
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().setDeduplicationSnapshotInterval(topic, 1000)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove deduplication snapshot interval
+
+To remove the topic-level deduplication snapshot interval, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics remove-deduplication-snapshot-interval my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/topics/:tenant/:namespace/:topic/deduplicationSnapshotInterval|operation/deleteDeduplicationSnapshotInterval?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().removeDeduplicationSnapshotInterval(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+### Configure inactive topic policies
+
+#### Get inactive topic policies
+
+To get the topic-level inactive topic policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-inactive-topic-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/topics/:tenant/:namespace/:topic/inactiveTopicPolicies|operation/getInactiveTopicPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getInactiveTopicPolicies(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Set inactive topic policies
+
+To set the topic-level inactive topic policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics set-inactive-topic-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/topics/:tenant/:namespace/:topic/inactiveTopicPolicies|operation/setInactiveTopicPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().setInactiveTopicPolicies(topic, inactiveTopicPolicies)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove inactive topic policies
+
+To remove the topic-level inactive topic policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics remove-inactive-topic-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/topics/:tenant/:namespace/:topic/inactiveTopicPolicies|operation/removeInactiveTopicPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().removeInactiveTopicPolicies(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+### Configure offload policies
+
+#### Get offload policies
+
+To get the topic-level offload policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics get-offload-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/topics/:tenant/:namespace/:topic/offloadPolicies|operation/getOffloadPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getOffloadPolicies(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Set offload policies
+
+To set the topic-level offload policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics set-offload-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/topics/:tenant/:namespace/:topic/offloadPolicies|operation/setOffloadPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().setOffloadPolicies(topic, offloadPolicies)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+#### Remove offload policies
+
+To remove the topic-level offload policies, use one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics remove-offload-policies my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/topics/:tenant/:namespace/:topic/offloadPolicies|operation/removeOffloadPolicies?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().removeOffloadPolicies(topic)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+## Manage non-partitioned topics
+You can use Pulsar [admin API](admin-api-overview.md) to create, delete and check the status of non-partitioned topics.
+
+### Create
+Non-partitioned topics must be explicitly created. When creating a new non-partitioned topic, you need to provide a name for the topic.
+
+By default, 60 seconds after creation, topics are considered inactive and deleted automatically to avoid generating trash data. To disable this feature, set `brokerDeleteInactiveTopicsEnabled` to `false`. To change the frequency of checking inactive topics, set `brokerDeleteInactiveTopicsFrequencySeconds` to a specific value.
+
+For more information about the two parameters, see [here](reference-configuration.md#broker).
+
+You can create non-partitioned topics in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+When you create non-partitioned topics with the [`create`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=create) command, you need to specify the topic name as an argument.
+
+```shell
+pulsar-admin topics create \
+    persistent://my-tenant/my-namespace/my-topic
+```
+
+:::note
+
+When you create a non-partitioned topic with the suffix '-partition-' followed by numeric value like 'xyz-topic-partition-x' for the topic name, if a partitioned topic with same suffix 'xyz-topic-partition-y' exists, then the numeric value(x) for the non-partitioned topic must be larger than the number of partitions(y) of the partitioned topic. Otherwise, you cannot create such a non-partitioned topic.
+
+:::
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/:schema/:tenant/:namespace/:topic|operation/createNonPartitionedTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topicName = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().createNonPartitionedTopic(topicName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete
+
+You can delete non-partitioned topics in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics delete \
+    persistent://my-tenant/my-namespace/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/:schema/:tenant/:namespace/:topic|operation/deleteTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().delete(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List
+
+You can get the list of topics under a given namespace in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics list tenant/namespace
+```
+
+Example output:
+
+```
+persistent://tenant/namespace/topic1
+persistent://tenant/namespace/topic2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace|operation/getList?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getList(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Stats
+
+
+You can check the current statistics of a given topic and its connected producers and consumers in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics stats \
+    persistent://test-tenant/namespace/topic \
+    --get-precise-backlog
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/stats|operation/getStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getStats(topic, false /* is precise backlog */);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example. For the description of topic stats, see [Pulsar statistics](administration-stats.md#topic-stats).
+
+```json
+{
+  "msgRateIn": 4641.528542257553,
+  "msgThroughputIn": 44663039.74947473,
+  "msgRateOut": 0,
+  "msgThroughputOut": 0,
+  "averageMsgSize": 1232439.816728665,
+  "storageSize": 135532389160,
+  "publishers": [
+    {
+      "msgRateIn": 57.855383881403576,
+      "msgThroughputIn": 558994.7078932219,
+      "averageMsgSize": 613135,
+      "producerId": 0,
+      "producerName": null,
+      "address": null,
+      "connectedSince": null
+    }
+  ],
+  "subscriptions": {
+    "my-topic_subscription": {
+      "msgRateOut": 0,
+      "msgThroughputOut": 0,
+      "msgBacklog": 116632,
+      "type": null,
+      "msgRateExpired": 36.98245516804671,
+      "consumers": []
+    }
+  },
+  "replication": {}
+}
+```
+
+### Internal stats
+
+You can check the detailed statistics of a topic. The following is an example. For the description of each internal topic stats, see [Pulsar statistics](administration-stats.md#topic-internal-stats).
+
+```json
+{
+  "entriesAddedCounter": 20449518,
+  "numberOfEntries": 3233,
+  "totalSize": 331482,
+  "currentLedgerEntries": 3233,
+  "currentLedgerSize": 331482,
+  "lastLedgerCreatedTimestamp": "2016-06-29 03:00:23.825",
+  "lastLedgerCreationFailureTimestamp": null,
+  "waitingCursorsCount": 1,
+  "pendingAddEntriesCount": 0,
+  "lastConfirmedEntry": "324711539:3232",
+  "state": "LedgerOpened",
+  "ledgers": [
+    {
+      "ledgerId": 324711539,
+      "entries": 0,
+      "size": 0
+    }
+  ],
+  "cursors": {
+    "my-subscription": {
+      "markDeletePosition": "324711539:3133",
+      "readPosition": "324711539:3233",
+      "waitingReadOp": true,
+      "pendingReadOps": 0,
+      "messagesConsumedCounter": 20449501,
+      "cursorLedger": 324702104,
+      "cursorLedgerLastEntry": 21,
+      "individuallyDeletedMessages": "[(324711539:3134โ€ฅ324711539:3136], (324711539:3137โ€ฅ324711539:3140], ]",
+      "lastLedgerSwitchTimestamp": "2016-06-29 01:30:19.313",
+      "state": "Open"
+    }
+  }
+}
+```
+
+You can get the internal stats for the partitioned topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics stats-internal \
+    persistent://test-tenant/namespace/topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/internalStats|operation/getInternalStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getInternalStats(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Manage partitioned topics
+You can use Pulsar [admin API](admin-api-overview.md) to create, update, delete and check the status of partitioned topics.
+
+### Create
+
+When creating a new partitioned topic, you need to provide a name and the number of partitions for the topic.
+
+:::note
+
+By default, if there are no messages 60 seconds after creation, topics are considered inactive and deleted automatically to avoid generating trash data. To disable this feature, set `brokerDeleteInactiveTopicsEnabled` to `false`. To change the frequency of checking inactive topics, set `brokerDeleteInactiveTopicsFrequencySeconds` to a specific value.
+
+:::
+
+For more information about the two parameters, see [here](reference-configuration.md#broker).
+
+You can create partitioned topics in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+When you create partitioned topics with the [`create-partitioned-topic`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=create-partitioned-topic) command, you need to specify the topic name as an argument and the number of partitions using the `-p` or `--partitions` flag.
+
+```shell
+pulsar-admin topics create-partitioned-topic \
+    persistent://my-tenant/my-namespace/my-topic \
+    --partitions 4
+```
+
+:::note
+
+If a non-partitioned topic with the suffix '-partition-' followed by a numeric value like 'xyz-topic-partition-10', you can not create a partitioned topic with name 'xyz-topic', because the partitions of the partitioned topic could override the existing non-partitioned topic. To create such partitioned topic, you have to delete that non-partitioned topic first.
+
+:::
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/:schema/:tenant/:namespace/:topic/partitions|operation/createPartitionedTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topicName = "persistent://my-tenant/my-namespace/my-topic";
+int numPartitions = 4;
+admin.topics().createPartitionedTopic(topicName, numPartitions);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Create missed partitions
+
+When topic auto-creation is disabled, and you have a partitioned topic without any partitions, you can use the [`create-missed-partitions`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=create-missed-partitions) command to create partitions for the topic.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+You can create missed partitions with the [`create-missed-partitions`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=create-missed-partitions) command and specify the topic name as an argument.
+
+```shell
+pulsar-admin topics create-missed-partitions \
+    persistent://my-tenant/my-namespace/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:namespace/:topic|operation/createMissedPartitions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topicName = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().createMissedPartitions(topicName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get metadata
+
+Partitioned topics are associated with metadata, you can view it as a JSON object. The following metadata field is available.
+
+Field | Description
+:-----|:-------
+`partitions` | The number of partitions into which the topic is divided.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+You can check the number of partitions in a partitioned topic with the [`get-partitioned-topic-metadata`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=get-partitioned-topic-metadata) subcommand.
+
+```shell
+pulsar-admin topics get-partitioned-topic-metadata \
+    persistent://my-tenant/my-namespace/my-topic
+```
+
+Example output:
+
+```json
+{
+  "partitions" : 4,
+  "deleted" : false
+}
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/partitions|operation/getPartitionedMetadata?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topicName = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getPartitionedTopicMetadata(topicName);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Update
+
+You can update the number of partitions for an existing partitioned topic. However, you can only increase the number of partitions. Decrementing the number of partitions would delete the topic, which is not supported in Pulsar.
+
+Producers and consumers can find the newly created partitions automatically.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+You can update partitioned topics with the [`update-partitioned-topic`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=update-partitioned-topic) command.
+
+```shell
+pulsar-admin topics update-partitioned-topic \
+    persistent://my-tenant/my-namespace/my-topic \
+    --partitions 8
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/:schema/:tenant/:cluster/:namespace/:destination/partitions|operation/updatePartitionedTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().updatePartitionedTopic(topic, numPartitions);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Delete
+You can delete partitioned topics with the [`delete-partitioned-topic`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=delete-partitioned-topic) command, REST API and Java.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics delete-partitioned-topic \
+    persistent://my-tenant/my-namespace/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/:schema/:topic/:namespace/:destination/partitions|operation/deletePartitionedTopic?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().deletePartitionedTopic(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### List
+
+You can get the list of partitioned topics under a given namespace in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics list-partitioned-topics tenant/namespace
+```
+
+Example output:
+
+```
+persistent://tenant/namespace/topic1
+persistent://tenant/namespace/topic2
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace|operation/getPartitionedTopicList?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getPartitionedTopicList(namespace);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Stats
+
+You can check the current statistics of a given partitioned topic and its connected producers and consumers in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics partitioned-stats \
+    persistent://test-tenant/namespace/topic \
+    --per-partition
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/partitioned-stats|operation/getPartitionedStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getPartitionedStats(topic, true /* per partition */, false /* is precise backlog */);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example. For the description of each topic stats, see [Pulsar statistics](administration-stats.md#topic-stats).
+
+Note that in the subscription JSON object, `chuckedMessageRate` is deprecated. Please use `chunkedMessageRate`. Both will be sent in the JSON for now.
+
+```json
+{
+  "msgRateIn" : 999.992947159793,
+  "msgThroughputIn" : 1070918.4635439808,
+  "msgRateOut" : 0.0,
+  "msgThroughputOut" : 0.0,
+  "bytesInCounter" : 270318763,
+  "msgInCounter" : 252489,
+  "bytesOutCounter" : 0,
+  "msgOutCounter" : 0,
+  "averageMsgSize" : 1070.926056966454,
+  "msgChunkPublished" : false,
+  "storageSize" : 270316646,
+  "backlogSize" : 200921133,
+  "publishers" : [ {
+    "msgRateIn" : 999.992947159793,
+    "msgThroughputIn" : 1070918.4635439808,
+    "averageMsgSize" : 1070.3333333333333,
+    "chunkedMessageRate" : 0.0,
+    "producerId" : 0
+  } ],
+  "subscriptions" : {
+    "test" : {
+      "msgRateOut" : 0.0,
+      "msgThroughputOut" : 0.0,
+      "bytesOutCounter" : 0,
+      "msgOutCounter" : 0,
+      "msgRateRedeliver" : 0.0,
+      "chuckedMessageRate" : 0,
+      "chunkedMessageRate" : 0,
+      "msgBacklog" : 144318,
+      "msgBacklogNoDelayed" : 144318,
+      "blockedSubscriptionOnUnackedMsgs" : false,
+      "msgDelayed" : 0,
+      "unackedMessages" : 0,
+      "msgRateExpired" : 0.0,
+      "lastExpireTimestamp" : 0,
+      "lastConsumedFlowTimestamp" : 0,
+      "lastConsumedTimestamp" : 0,
+      "lastAckedTimestamp" : 0,
+      "consumers" : [ ],
+      "isDurable" : true,
+      "isReplicated" : false
+    }
+  },
+  "replication" : { },
+  "metadata" : {
+    "partitions" : 3
+  },
+  "partitions" : { }
+}
+```
+
+### Internal stats
+
+You can check the detailed statistics of a partitioned topic. The following is an example. For the description of each internal topic stats, see [Pulsar statistics](administration-stats.md#topic-internal-stats).
+
+```json
+{
+  "entriesAddedCounter": 20449518,
+  "numberOfEntries": 3233,
+  "totalSize": 331482,
+  "currentLedgerEntries": 3233,
+  "currentLedgerSize": 331482,
+  "lastLedgerCreatedTimestamp": "2016-06-29 03:00:23.825",
+  "lastLedgerCreationFailureTimestamp": null,
+  "waitingCursorsCount": 1,
+  "pendingAddEntriesCount": 0,
+  "lastConfirmedEntry": "324711539:3232",
+  "state": "LedgerOpened",
+  "ledgers": [
+    {
+      "ledgerId": 324711539,
+      "entries": 0,
+      "size": 0
+    }
+  ],
+  "cursors": {
+    "my-subscription": {
+      "markDeletePosition": "324711539:3133",
+      "readPosition": "324711539:3233",
+      "waitingReadOp": true,
+      "pendingReadOps": 0,
+      "messagesConsumedCounter": 20449501,
+      "cursorLedger": 324702104,
+      "cursorLedgerLastEntry": 21,
+      "individuallyDeletedMessages": "[(324711539:3134โ€ฅ324711539:3136], (324711539:3137โ€ฅ324711539:3140], ]",
+      "lastLedgerSwitchTimestamp": "2016-06-29 01:30:19.313",
+      "state": "Open"
+    }
+  }
+}
+```
+
+You can get the internal stats for the partitioned topic in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics partitioned-stats-internal \
+    persistent://test-tenant/namespace/topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/partitioned-internalStats|operation/getPartitionedInternalStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.topics().getPartitionedInternalStats(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+
+## Manage subscriptions
+
+You can use [Pulsar admin API](admin-api-overview.md) to create, check, and delete subscriptions.
+
+### Create subscription
+
+You can create a subscription for a topic using one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics create-subscription \
+    --subscription my-subscription \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/persistent/:tenant/:namespace/:topic/subscription/:subscription|operation/createSubscriptions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subscriptionName = "my-subscription";
+admin.topics().createSubscription(topic, subscriptionName, MessageId.latest);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Get subscription
+
+You can check all subscription names for a given topic using one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics subscriptions persistent://test-tenant/ns1/tp1
+```
+
+Example output:
+
+```
+my-subscription
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/subscriptions|operation/getSubscriptions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+admin.topics().getSubscriptions(topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+### Unsubscribe subscription
+
+When a subscription does not process messages anymore, you can unsubscribe it using one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin topics unsubscribe \
+    --subscription my-subscription \
+    persistent://test-tenant/ns1/tp1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|DELETE|/admin/v2/namespaces/:tenant/:namespace/:topic/subscription/:subscription|operation/deleteSubscription?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+String topic = "persistent://my-tenant/my-namespace/my-topic";
+String subscriptionName = "my-subscription";
+admin.topics().deleteSubscription(topic, subscriptionName);
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-transactions.md b/versioned_docs/version-4.0.x/admin-api-transactions.md
new file mode 100644
index 000000000000..ec5e6e5bd976
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-transactions.md
@@ -0,0 +1,726 @@
+---
+id: admin-api-transactions
+title: Manage transactions
+sidebar_label: "Transactions"
+description: Learn how to manage transactions using Pulsar CLI and admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+:::tip
+
+This page only shows **some frequently used operations**.
+
+- For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+- For the latest and complete information about `REST API`, including parameters, responses, samples, and more, see {@inject: rest:REST:/} API doc.
+
+- For the latest and complete information about `Java admin API`, including classes, methods, descriptions, and more, see [Java admin API doc](/api/admin/).
+
+:::
+
+## Transaction resources
+
+### GetSlowTransactions
+
+In the production environment, there may be some long-lasting transactions that have never been completed. You can get these slow transactions that have survived over a certain time under a coordinator or all coordinators in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions slow-transactions -c 1 -t 1s
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/slowTransactions/:timeout|operation/getSlowTransactions?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getSlowTransactionsByCoordinatorId(coordinatorId, timeout, timeUnit)
+//Or get slow transactions from all coordinators
+admin.transactions().getSlowTransactions(timeout, timeUnit)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+  "(0,3)": {
+    "txnId": "(0,3)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122474,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,2)": {
+    "txnId": "(0,2)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122471,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,5)": {
+    "txnId": "(0,5)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122478,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,4)": {
+    "txnId": "(0,4)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122476,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,7)": {
+    "txnId": "(0,7)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122482,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,10)": {
+    "txnId": "(0,10)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122488,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,6)": {
+    "txnId": "(0,6)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122480,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,9)": {
+    "txnId": "(0,9)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122486,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,8)": {
+    "txnId": "(0,8)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122484,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  },
+  "(0,11)": {
+    "txnId": "(0,11)",
+    "status": "OPEN",
+    "openTimestamp": 1658120122490,
+    "timeoutAt": 300000,
+    "producedPartitions": {},
+    "ackedPartitions": {}
+  }
+}
+```
+
+### ScaleTransactionCoordinators
+
+When the performance of transactions reaches a bottleneck due to the insufficient number of transaction coordinators, you can scale the number of the transaction coordinators in the following ways.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions scale-transactionCoordinators -r 17
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/transactionCoordinator/:replicas|operation/scaleTransactionCoordinators?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().scaleTransactionCoordinators(replicas);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+## Transaction stats
+
+### Get transaction metadata
+
+The transaction metadata that can be retrieved include:
+* **txnId:** The ID of this transaction.
+* **status:** The status of this transaction.
+* **openTimestamp:** The open time of this transaction.
+* **timeoutAt:** The timeout of this transaction.
+* **producedPartitions:** The partitions or topics that messages have been sent to with this transaction.
+* **ackedPartitions:** The partitions or topics where messages have been acknowledged with this transaction.
+
+Use one of the following ways to get your transaction metadata.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions transaction-metadata -m 1 -l 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/transactionMetadata/:mostSigBits/:leastSigBits|operation/getTransactionMetadata?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getTransactionMetadata(txnID);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+ "txnId" : "(1,18)",
+ "status" : "ABORTING",
+ "openTimestamp" : 1656592983374,
+ "timeoutAt" : 5000,
+ "producedPartitions" : {
+   "my-topic" : {
+     "startPosition" : "127:4959",
+     "aborted" : true
+   }
+ },
+ "ackedPartitions" : {
+   "my-topic" : {
+     "mysubName" : {
+       "cumulativeAckPosition" : null
+     }
+   }
+ }
+}
+```
+
+### Get transaction stats in transaction pending ack
+
+The transaction stats in transaction pending ack that can be retrieved include:
+* **cumulativeAckPosition:** The position that this transaction cumulatively acknowledges in this subscription.
+
+Use one of the following ways to get transaction stats in pending ack:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions transaction-in-pending-ack-stats -m 1 -l 1 -t my-topic -s mysubname
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/transactionInPendingAckStats/:tenant/:namespace/:topic/:subName/:mostSigBits/:leastSigBits|operation/getTransactionInPendingAckStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getTransactionInPendingAckStats(txnID, topic, subname);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned value.
+
+```json
+{
+ "cumulativeAckPosition" : "137:49959"
+ }
+```
+
+### Get transaction stats in transaction buffer
+
+The transaction stats in the transaction buffer that can be retrieved include:
+* **startPosition:** The start position of this transaction in the transaction buffer.
+* **aborted:** The flag of whether this transaction has been aborted.
+
+Use one of the following ways to get transaction stats in transaction buffer:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions transaction-in-buffer-stats -m 1 -l 1 -t my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/transactionInBufferStats/:tenant/:namespace/:topic/:mostSigBits/:leastSigBits|operation/getTransactionInBufferStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getTransactionInBufferStatsAsync(txnID, topic);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+ "startPosition" : "137:49759",
+ "aborted" : false
+}
+```
+
+## Transaction coordinator stats
+
+The transaction coordinator (TC) is a module inside a Pulsar broker. It maintains the entire life cycle of transactions and handles transaction timeout.
+
+### Get coordinator stats
+The transaction coordinator stats that can be retrieved include:
+* **state:**  The state of this transaction coordinator.
+* **leastSigBit:s** The sequence ID of this transaction coordinator.
+* **lowWaterMark:** The low watermark of this transaction coordinator.
+* **ongoingTxnSize:** The total number of ongoing transactions in this transaction coordinator.
+* **recoverStartTime:** The start timestamp of transaction coordinator recovery. `0L` means no startup.
+* **recoverEndTime:** The end timestamp of transaction coordinator recovery. `0L` means no startup.
+
+Use one of the following ways to get transaction coordinator stats:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions coordinator-stats -c 1
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/coordinatorStats|operation/getCoordinatorStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getCoordinatorStatsById(coordinatorId);
+//Or get all coordinator stats.
+admin.transactions().getCoordinatorStats();
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+  "state" : "Ready",
+  "leastSigBits" : 1,
+  "lowWaterMark" : 0,
+  "ongoingTxnSize" : 0,
+  "recoverStartTime" : 1657021892377,
+  "recoverEndTime" : 1657021892378
+}
+```
+
+### Get coordinator internal stats
+
+The coordinator's internal stats that can be retrieved include:
+* **transactionLogStats:** The stats of the transaction coordinator log.
+* **managedLedgerName:** The name of the managed ledger where the transaction coordinator log is stored.
+* **managedLedgerInternalStats:** The internal stats of the managed ledger where the transaction coordinator log is stored. See `[managedLedgerInternalStats](admin-api-topics.md#get-internal-stats)` for more details.
+
+Use one of the following ways to get coordinator's internal stats:
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions coordinator-internal-stats -c 1 -m
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/coordinatorInternalStats/:coordinatorId|operation/getCoordinatorInternalStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getCoordinatorInternalStats(coordinatorId, metadata);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+ "transactionLogStats" : {
+   "managedLedgerName" : "pulsar/system/persistent/__transaction_log_1",
+   "managedLedgerInternalStats" : {
+     "entriesAddedCounter" : 3,
+     "numberOfEntries" : 3,
+     "totalSize" : 63,
+     "currentLedgerEntries" : 3,
+     "currentLedgerSize" : 63,
+     "lastLedgerCreatedTimestamp" : "2022-06-30T18:18:05.88+08:00",
+     "waitingCursorsCount" : 0,
+     "pendingAddEntriesCount" : 0,
+     "lastConfirmedEntry" : "13:2",
+     "state" : "LedgerOpened",
+     "ledgers" : [ {
+       "ledgerId" : 13,
+       "entries" : 0,
+       "size" : 0,
+       "offloaded" : false,
+       "metadata" : "LedgerMetadata{formatVersion=3, ensembleSize=1, writeQuorumSize=1, ackQuorumSize=1, state=CLOSED, length=63, lastEntryId=2, digestType=CRC32C, password=OMITTED, ensembles={0=[10.20.240.119:3181]}, customMetadata={component=base64:bWFuYWdlZC1sZWRnZXI=, pulsar/managed-ledger=base64:cHVsc2FyL3N5c3RlbS9wZXJzaXN0ZW50L19fdHJhbnNhY3Rpb25fbG9nXzE=, application=base64:cHVsc2Fy}}",
+       "underReplicated" : false
+     } ],
+     "cursors" : {
+       "transaction.subscription" : {
+         "markDeletePosition" : "13:2",
+         "readPosition" : "13:3",
+         "waitingReadOp" : false,
+         "pendingReadOps" : 0,
+         "messagesConsumedCounter" : 3,
+         "cursorLedger" : 22,
+         "cursorLedgerLastEntry" : 1,
+         "individuallyDeletedMessages" : "[]",
+         "lastLedgerSwitchTimestamp" : "2022-06-30T18:18:05.932+08:00",
+         "state" : "Open",
+         "numberOfEntriesSinceFirstNotAckedMessage" : 1,
+         "totalNonContiguousDeletedMessagesRange" : 0,
+         "subscriptionHavePendingRead" : false,
+         "subscriptionHavePendingReplayRead" : false,
+         "properties" : { }
+       }
+     }
+   }
+ }
+}
+```
+
+## Transaction pending ack stats
+
+Pending ack maintains message acknowledgments within a transaction before a transaction completes. If a message is in the pending acknowledge state, the message cannot be acknowledged by other transactions until the message is removed from the pending acknowledge state.
+
+### Get transaction pending ack stats
+
+The transaction pending ack state stats that can be retrieved include:
+* **state:** The state of this transaction coordinator.
+* **lowWaterMark:** The low watermark of this transaction coordinator.
+* **ongoingTxnSize:** The total number of ongoing transactions in this transaction coordinator.
+* **recoverStartTime:** The start timestamp of transaction pendingAck recovery. `0L` means no startup.
+* **recoverEndTime:** The end timestamp of transaction pendingAck recovery. `0L` means no startup.
+
+Use one of the following ways to get transaction pending ack stats:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin.transactions()s pending-ack-stats -t my-topic -s mysubName -l
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/pendingAckStats/:tenant/:namespace:/:topic:/:subName|operation/getPendingAckStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getPendingAckStats(topic, subName, lowWaterMarks)
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+  "state" : "Ready",
+  "lowWaterMarks" : {
+    "1" : 0
+  },
+  "ongoingTxnSize" : 1,
+  "recoverStartTime" : 1657021899202,
+  "recoverEndTime" : 1657021899203
+}
+```
+
+### Get transaction pending ack internal stats
+
+The transaction pending ack internal stats that can be retrieved include:
+* **transactionLogStats:** The stats of the transaction pending ack log.
+* **managedLedgerName:** The name of the managed ledger where the transaction pending ack log is stored.
+* **managedLedgerInternalStats:** The internal stats of the managed ledger where the transaction coordinator log is stored. See `[managedLedgerInternalStats](admin-api-topics.md#get-internal-stats)` for more details.
+
+Use one of the following ways to get transaction pending ack internal stats:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions pending-ack-internal-stats -t my-topic -s mysubName -m
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/pendingAckInternalStats/:tenant/:namespace:/:topic:/:subName|operation/getPendingAckInternalStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getPendingAckInternalStats(topic, subName, boolean metadata);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+ "pendingAckLogStats" : {
+   "managedLedgerName" : "public/default/persistent/my-topic-mysubName__transaction_pending_ack",
+   "managedLedgerInternalStats" : {
+     "entriesAddedCounter" : 2247,
+     "numberOfEntries" : 2247,
+     "totalSize" : 37212,
+     "currentLedgerEntries" : 104,
+     "currentLedgerSize" : 1732,
+     "lastLedgerCreatedTimestamp" : "2022-06-30T19:02:09.746+08:00",
+     "waitingCursorsCount" : 0,
+     "pendingAddEntriesCount" : 52,
+     "lastConfirmedEntry" : "64:51",
+     "state" : "LedgerOpened",
+     "ledgers" : [ {
+       "ledgerId" : 56,
+       "entries" : 2195,
+       "size" : 36346,
+       "offloaded" : false,
+       "metadata" : "LedgerMetadata{formatVersion=3, ensembleSize=1, writeQuorumSize=1, ackQuorumSize=1, state=CLOSED, length=36346, lastEntryId=2194, digestType=CRC32C, password=OMITTED, ensembles={0=[10.20.240.119:3181]}, customMetadata={component=base64:bWFuYWdlZC1sZWRnZXI=, pulsar/managed-ledger=base64:cHVibGljL2RlZmF1bHQvcGVyc2lzdGVudC9teS10b3BpYy1teXN1Yk5hbWVfX3RyYW5zYWN0aW9uX3BlbmRpbmdfYWNr, application=base64:cHVsc2Fy}}",
+       "underReplicated" : false
+     }, {
+       "ledgerId" : 64,
+       "entries" : 0,
+       "size" : 0,
+       "offloaded" : false,
+       "metadata" : "LedgerMetadata{formatVersion=3, ensembleSize=1, writeQuorumSize=1, ackQuorumSize=1, state=CLOSED, length=866, lastEntryId=51, digestType=CRC32C, password=OMITTED, ensembles={0=[10.20.240.119:3181]}, customMetadata={component=base64:bWFuYWdlZC1sZWRnZXI=, pulsar/managed-ledger=base64:cHVibGljL2RlZmF1bHQvcGVyc2lzdGVudC9teS10b3BpYy1teXN1Yk5hbWVfX3RyYW5zYWN0aW9uX3BlbmRpbmdfYWNr, application=base64:cHVsc2Fy}}",
+       "underReplicated" : false
+     } ],
+     "cursors" : {
+       "__pending_ack_state" : {
+         "markDeletePosition" : "56:-1",
+         "readPosition" : "56:0",
+         "waitingReadOp" : false,
+         "pendingReadOps" : 0,
+         "messagesConsumedCounter" : 0,
+         "cursorLedger" : 57,
+         "cursorLedgerLastEntry" : 0,
+         "individuallyDeletedMessages" : "[]",
+         "lastLedgerSwitchTimestamp" : "2022-06-30T18:55:26.842+08:00",
+         "state" : "Open",
+         "numberOfEntriesSinceFirstNotAckedMessage" : 1,
+         "totalNonContiguousDeletedMessagesRange" : 0,
+         "subscriptionHavePendingRead" : false,
+         "subscriptionHavePendingReplayRead" : false,
+         "properties" : { }
+       }
+     }
+   }
+ }
+}
+```
+
+### Get position stats in pending ack
+
+The position stats in pending ack include:
+* **PendingAck** The position is in pending ack stats.
+* **MarkDelete** The position is already acknowledged.
+* **NotInPendingAck** The position is not acknowledged within a transaction.
+* **PendingAckNotReady** The pending ack has not been initialized.
+* **InvalidPosition** The position is invalid, for example, batch index > batch size.
+
+If you want to know whether the position has been acknowledged, you can use one of the following ways to get position stats pending ack:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions position-stats-in-pending-ack -t my-topic -s mysubName -l 15 -e 6
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/pendingAckStats
+/:tenant/:namespace:/:topic:/:subName/:ledgerId/:entryId?batchIndex=batchIndex|operation/getPositionStatsInPendingAck?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getPositionStatsInPendingAckAsync(topic, subName, ledgerId, entryId, lowWaterMarks);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+  "State" : "MarkDelete"
+}
+```
+
+## Transaction buffer stats
+
+Transaction buffer handles messages produced to a topic partition within a transaction.
+The messages in the transaction buffer are not visible to consumers until the transactions are committed. The messages in the transaction buffer are discarded when the transactions are aborted.
+
+### Get transaction buffer stats
+
+The transaction buffer stats that can be retrieved include:
+* **state:** The state of this transaction buffer.
+* **maxReadPosition:** The maximum read position of this transaction buffer.
+* **lastSnapshotTimestamps:** The last snapshot timestamp of this transaction buffer.
+* **lowWaterMarks (Optional):** The low watermark details of the transaction buffer.
+* **ongoingTxnSize:** The total number of ongoing transactions in this transaction buffer.
+* **recoverStartTime:** The start timestamp of transaction buffer recovery. `0L` means no startup.
+* **recoverEndTime:** The end timestamp of transaction buffer recovery. `0L` means no startup.
+
+Use one of the following ways to get transaction buffer stats:
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+ defaultValue="pulsar-admin"
+ values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+```shell
+pulsar-admin transactions transaction-buffer-stats -t my-topic -l
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v3/transactions/transactionBufferStats/:tenant/:namespace:/:topic:/:subName|operation/getTransactionBufferStats?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java">
+
+```java
+admin.transactions().getTransactionBufferStats(topic, lowWaterMarks);
+```
+
+</TabItem>
+
+</Tabs>
+````
+
+The following is an example of the returned values.
+
+```json
+{
+  "state" : "Ready",
+  "maxReadPosition" : "38:101",
+  "lastSnapshotTimestamps" : 1657021903534,
+  "lowWaterMarks" : {
+    "1" : -1,
+    "2" : -1
+  },
+  "ongoingTxnSize" : 0,
+  "recoverStartTime" : 1657021892850,
+  "recoverEndTime" : 1657021893372
+}
+```
diff --git a/versioned_docs/version-4.0.x/admin-api-tutorial.md b/versioned_docs/version-4.0.x/admin-api-tutorial.md
new file mode 100644
index 000000000000..17665cbc2d88
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-tutorial.md
@@ -0,0 +1,85 @@
+---
+id: admin-api-tutorial
+title: Tutorial
+sidebar_label: "Tutorial"
+description: Learn how to configure Pulsar admin interfaces if authentication is enabled in your Pulsar instance.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+## Admin setup
+
+Each of the three admin interfaces (the `pulsar-admin` CLI tool, the [REST API](reference-rest-api-overview.md), and the [Java admin API](/api/admin/)) requires some special setup if you have enabled authentication in your Pulsar instance.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+If you have enabled authentication, you need to provide an auth configuration to use the `pulsar-admin` tool. By default, the configuration for the `pulsar-admin` tool is in the [`conf/client.conf`](https://github.com/apache/pulsar/blob/master/conf/client.conf) file. The following are the available parameters:
+
+|Name|Description|Default|
+|----|-----------|-------|
+|webServiceUrl|The web URL for the cluster.|http://localhost:8080/|
+|brokerServiceUrl|The Pulsar protocol URL for the cluster.|pulsar://localhost:6650/|
+|authPlugin|The authentication plugin.| |
+|authParams|The authentication parameters for the cluster, as a comma-separated string.| |
+|useTls|Whether or not TLS authentication will be enforced in the cluster.|false|
+|tlsAllowInsecureConnection|Accept untrusted TLS certificate from client.|false|
+|tlsTrustCertsFilePath|Path for the trusted TLS certificate file.| |
+
+</TabItem>
+<TabItem value="REST API">
+
+You can find details for the REST API exposed by Pulsar brokers in the [REST API doc](pathname:///admin-rest-api/?version=@pulsar:version_number@).
+
+If you want to test REST APIs in postman, you can use the REST API JSON files [here](pathname:///swagger/).
+
+</TabItem>
+<TabItem value="Java">
+
+To use the Java admin API, instantiate a [PulsarAdmin](/api/admin/org/apache/pulsar/client/admin/PulsarAdmin) object, and specify a URL for a Pulsar broker and a [PulsarAdminBuilder](/api/admin/org/apache/pulsar/client/admin/PulsarAdminBuilder) object. The following is a minimal example using `localhost`.
+
+```java
+String url = "http://localhost:8080";
+// Pass auth-plugin class fully-qualified name if Pulsar-security enabled
+String authPluginClassName = "com.org.MyAuthPluginClass";
+// Pass auth-param if auth-plugin class requires it
+String authParams = "param1=value1";
+boolean tlsAllowInsecureConnection = false;
+String tlsTrustCertsFilePath = null;
+PulsarAdmin admin = PulsarAdmin.builder()
+    .authentication(authPluginClassName,authParams)
+    .serviceHttpUrl(url)
+    .tlsTrustCertsFilePath(tlsTrustCertsFilePath)
+    .allowTlsInsecureConnection(tlsAllowInsecureConnection)
+    .build();
+```
+
+If you use multiple brokers, you can use multi-host like Pulsar service. For example,
+
+```java
+String url = "http://localhost:8080,localhost:8081,localhost:8082";
+// Below are the same to the line 2 - line 13 in the code snippet above
+// Pass auth-plugin class fully-qualified name if Pulsar-security enabled
+String authPluginClassName = "com.org.MyAuthPluginClass";
+// Pass auth-param if auth-plugin class requires it
+String authParams = "param1=value1";
+boolean tlsAllowInsecureConnection = false;
+String tlsTrustCertsFilePath = null;
+PulsarAdmin admin = PulsarAdmin.builder()
+    .authentication(authPluginClassName,authParams)
+    .serviceHttpUrl(url)
+    .tlsTrustCertsFilePath(tlsTrustCertsFilePath)
+    .allowTlsInsecureConnection(tlsAllowInsecureConnection)
+    .build();
+```
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/admin-api-use-cases.md b/versioned_docs/version-4.0.x/admin-api-use-cases.md
new file mode 100644
index 000000000000..87d5704a488b
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-api-use-cases.md
@@ -0,0 +1,37 @@
+---
+id: admin-api-use-cases
+title: Pulsar admin interfaces - Use cases
+sidebar_label: "Use cases"
+description: Get a comprehensive understanding of use cases of Pulsar admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+Pulsar admin APIs are one of the best productivity tools. You can perform various tasks with them, for example:
+
+- Administering Pulsar instances easily by creating, updating, or deleting objects with a single line of command, which increases management efficiency.
+
+- Monitoring Pulsar or troubleshooting issues by getting the status and information about Pulsar clusters, which reduces maintenance costs.
+
+- Facilitating the workflow of application development by implementing admin interface clients using your preferred languages (e.g., Go, Python, C++) based on REST APIs, which enhances developer experiences.
+
+![Use cases of Pulsar admin APIs](/assets/admin-api-use-cases.svg)
+
+### Related topics
+
+- To understand basics, see [Pulsar admin API - Overview](admin-api-overview.md)
+
+- To learn common administrative tasks, see [Pulsar admin API - Features](admin-api-features.md).
+
+- To perform administrative operations, see [Pulsar admin API - Tools](admin-api-tools.md).
+
+- To get up quickly, see [Pulsar admin API - Get started](admin-get-started.md).
+
+- To check the detailed usage, see the API references below.
+
+  - [Java admin API](/api/admin/)
+
+  - [REST API](reference-rest-api-overview.md)
diff --git a/versioned_docs/version-4.0.x/admin-get-started.md b/versioned_docs/version-4.0.x/admin-get-started.md
new file mode 100644
index 000000000000..3e1ccc63cf71
--- /dev/null
+++ b/versioned_docs/version-4.0.x/admin-get-started.md
@@ -0,0 +1,529 @@
+---
+id: admin-api-get-started
+title: Get started
+sidebar_label: "Get started"
+description: Learn how to manage topics in Pulsar using the Pulsar admin CLI or Pulsar admin APIs.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+This guide walks you through the quickest way to get started with the following methods to manage topics. 
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+[pulsar-admin CLI](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/) is a command-line tool and is available in the bin folder of your Pulsar installation.
+
+</TabItem>
+<TabItem value="REST API">
+
+[REST API](pathname:///admin-rest-api/?version=@pulsar:version_number@) belongs to HTTP calls, which are made against the admin APIs provided by brokers. In addition, both the Java admin API and pulsar-admin CLI use the REST API.
+
+</TabItem>
+<TabItem value="Java">
+
+[Java admin API](/api/admin/) is a programmable interface written in Java.
+
+</TabItem>
+
+</Tabs>
+````
+
+Check the detailed steps below.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"},{"label":"Java","value":"Java"}]}>
+<TabItem value="pulsar-admin">
+
+To manage topics using pulsar-admin CLI, complte the following steps.
+
+1. Set the service URL.
+
+2. Create a partitioned topic.
+
+3. Update the number of a partition.
+
+4. Produce messages to the topic.
+
+5. Check the stats of the topic.
+
+6. Delete the topic.
+
+**Prerequisites**
+
+- Install and start Pulsar standalone. This tutorial runs Pulsar 2.11 as an example.
+
+**Steps**
+
+**Step 1:** Set the service URLs to point to the broker service in [client.conf](https://github.com/apache/pulsar/blob/master/conf/client.conf).
+
+    ```bash
+    webServiceUrl=http://localhost:8080/
+    brokerServiceUrl=pulsar://localhost:6650/
+    ```
+
+**Step 2:** Create a persistent topic named _test-topic-1_ with 6 partitions.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-admin topics create-partitioned-topic \
+    persistent://public/default/test-topic-1 \
+    --partitions 6
+    ```
+
+    **Output**
+
+    There is no output. You can check the status of the topic in Step 5.
+
+**Step 3:** Update the number of the partition to 8.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-admin topics update-partitioned-topic \
+    persistent://public/default/test-topic-1 \
+    --partitions 8
+    ```
+
+    **Output**
+
+    There is no output. You can check the number of partitions in Step 5.
+
+**Step 4:** Produce some messages to the partitioned topic _test-topic-1_.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-perf produce -u pulsar://localhost:6650 -r 1000 -i 1000 persistent://public/default/test-topic-1
+    ```
+
+    **Output**
+
+    ```bash
+    2023-03-07T15:33:56,832+0800 [main] INFO  org.apache.pulsar.testclient.PerformanceProducer - Starting Pulsar perf producer with config: {
+      "confFile" : "/Users/yu/apache-pulsar-2.11.0/conf/client.conf",
+      "serviceURL" : "pulsar://localhost:6650",
+      "authPluginClassName" : "",
+      "authParams" : "",
+      "tlsTrustCertsFilePath" : "",
+      "tlsAllowInsecureConnection" : false,
+      "tlsHostnameVerificationEnable" : false,
+      "maxConnections" : 1,
+      "statsIntervalSeconds" : 1000,
+      "ioThreads" : 1,
+      "enableBusyWait" : false,
+      "listenerName" : null,
+      "listenerThreads" : 1,
+      "maxLookupRequest" : 50000,
+      "topics" : [ "persistent://public/default/test-topic-1" ],
+      "numTestThreads" : 1,
+      "msgRate" : 1000,
+      "msgSize" : 1024,
+      "numTopics" : 1,
+    "numProducers" : 1,
+      "separator" : "-",
+      "sendTimeout" : 0,
+      "producerName" : null,
+      "adminURL" : "http://localhost:8080/",
+
+    ...
+
+    2023-03-07T15:35:03,769+0800 [Thread-0] INFO  org.apache.pulsar.testclient.PerformanceProducer - Aggregated latency stats --- Latency: mean:   8.931 ms - med:   3.775 - 95pct:  32.144 - 99pct:  98.432 - 99.9pct: 216.088 - 99.99pct: 304.807 - 99.999pct: 349.391 - Max: 351.235
+    ```
+
+**Step 5:** Check the internal stats of the partitioned topic _test-topic-1_.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-admin topics partitioned-stats-internal \
+    persistent://public/default/test-topic-1
+    ```
+
+    **Output**
+
+    Below is a part of the output. For detailed explanations of topic stats, see Pulsar statistics.
+
+    ```bash
+    {
+      "metadata" : {
+        "partitions" : 8
+      },
+      "partitions" : {
+        "persistent://public/default/test-topic-1-partition-1" : {
+          "entriesAddedCounter" : 4213,
+          "numberOfEntries" : 4213,
+          "totalSize" : 8817693,
+          "currentLedgerEntries" : 4212,
+          "currentLedgerSize" : 8806289,
+          "lastLedgerCreatedTimestamp" : "2023-03-07T15:33:59.367+08:00",
+          "waitingCursorsCount" : 0,
+          "pendingAddEntriesCount" : 0,
+          "lastConfirmedEntry" : "65:4211",
+          "state" : "LedgerOpened",
+          "ledgers" : [ {
+            "ledgerId" : 49,
+            "entries" : 1,
+            "size" : 11404,
+            "offloaded" : false,
+            "underReplicated" : false
+          }, {
+            "ledgerId" : 65,
+            "entries" : 0,
+            "size" : 0,
+            "offloaded" : false,
+            "underReplicated" : false
+          } ],
+          "cursors" : {
+            "test-subscriptio-1" : {
+              "markDeletePosition" : "49:-1",
+              "readPosition" : "49:0",
+              "waitingReadOp" : false,
+              "pendingReadOps" : 0,
+              "messagesConsumedCounter" : 0,
+              "cursorLedger" : -1,
+              "cursorLedgerLastEntry" : -1,
+      "individuallyDeletedMessages" : "[]",
+              "lastLedgerSwitchTimestamp" : "2023-03-06T16:41:32.801+08:00",
+              "state" : "NoLedger",
+              "numberOfEntriesSinceFirstNotAckedMessage" : 1,
+              "totalNonContiguousDeletedMessagesRange" : 0,
+              "subscriptionHavePendingRead" : false,
+              "subscriptionHavePendingReplayRead" : false,
+              "properties" : { }
+            },
+            "test-subscription-1" : {
+              "markDeletePosition" : "49:-1",
+              "readPosition" : "49:0",
+              "waitingReadOp" : false,
+              "pendingReadOps" : 0,
+              "messagesConsumedCounter" : 0,
+              "cursorLedger" : -1,
+              "cursorLedgerLastEntry" : -1,
+              "individuallyDeletedMessages" : "[]",
+              "lastLedgerSwitchTimestamp" : "2023-03-06T16:41:32.801+08:00",
+              "state" : "NoLedger",
+              "numberOfEntriesSinceFirstNotAckedMessage" : 1,
+              "totalNonContiguousDeletedMessagesRange" : 0,
+              "subscriptionHavePendingRead" : false,
+              "subscriptionHavePendingReplayRead" : false,
+              "properties" : { }
+            }
+          },
+          "schemaLedgers" : [ ],
+          "compactedLedger" : {
+            "ledgerId" : -1,
+            "entries" : -1,
+            "size" : -1,
+            "offloaded" : false,
+            "underReplicated" : false
+          }
+        },
+    ...
+    ```
+
+**Step 6:** Delete the topic _test-topic-1_.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-admin topics delete-partitioned-topic persistent://public/default/test-topic-1
+    ```
+
+    **Output**
+
+    There is no output. You can verify whether the _test-topic-1_ exists or not using the following command.
+
+    **Input**
+
+    List topics in `public/default` namespace.
+
+    ```bash
+    bin/pulsar-admin topics list public/default
+    ```
+
+</TabItem>
+<TabItem value="REST API">
+
+To manage topics using REST API, complete the following steps.
+
+1. Create a partitioned topic
+
+2. Update the number of a partition.
+
+3. Produce messages to the topic.
+
+4. Check the stats of the topic.
+
+5. Delete the topic.
+
+**Prerequisites**
+
+- Install and start Pulsar standalone. This tutorial runs Pulsar 2.11 as an example.
+
+**Steps**
+
+**Step 1:** Create a persistent topic named _test-topic-2_ with 4 partitions.
+
+    **Input**
+
+    ```bash
+    curl -X PUT http://localhost:8080/admin/v2/persistent/public/default/test-topic-2/partitions -H 'Content-Type: application/json' -d "4"
+    ```
+
+    **Output**
+
+    There is no output. You can check the topic in Step 4.
+
+**Step 2:** Update the number of the partition to 5.
+
+    **Input**
+
+    ```bash
+    curl -X POST http://localhost:8080/admin/v2/persistent/public/default/test-topic-2/partitions -H 'Content-Type: application/json' -d "5"
+    ```
+
+    **Output**
+
+    There is no output. You can check the status of the topic in Step 4.
+
+**Step 3:** Produce some messages to the partitioned topic _test-topic-2_.
+
+    **Input**
+
+    ```bash
+    bin/pulsar-perf produce -u pulsar://localhost:6650 -r 1000 -i 1000 persistent://public/default/test-topic-2
+    ```
+
+    **Output**
+
+    ```bash
+    2023-03-08T15:47:06,268+0800 [main] INFO  org.apache.pulsar.testclient.PerformanceProducer - Starting Pulsar perf producer with config: {
+      "confFile" : "/Users/yu/apache-pulsar-2.11.0/conf/client.conf",
+      "serviceURL" : "pulsar://localhost:6650",
+      "authPluginClassName" : "",
+      "authParams" : "",
+      "tlsTrustCertsFilePath" : "",
+      "tlsAllowInsecureConnection" : false,
+      "tlsHostnameVerificationEnable" : false,
+      "maxConnections" : 1,
+      "statsIntervalSeconds" : 1000,
+      "ioThreads" : 1,
+      "enableBusyWait" : false,
+      "listenerName" : null,
+      "listenerThreads" : 1,
+      "maxLookupRequest" : 50000,
+      "topics" : [ "persistent://public/default/test-topic-2" ],
+      "numTestThreads" : 1,
+      "msgRate" : 1000,
+      "msgSize" : 1024,
+      "numTopics" : 1,
+    "numProducers" : 1,
+      "separator" : "-",
+      "sendTimeout" : 0,
+      "producerName" : null,
+      "adminURL" : "http://localhost:8080/",
+      "deprecatedAuthPluginClassName" : null,
+      "maxOutstanding" : 0,
+      "maxPendingMessagesAcrossPartitions" : 0,
+      "partitions" : null,
+      "numMessages" : 0,
+      "compression" : "NONE",
+      "payloadFilename" : null,
+      "payloadDelimiter" : "\\n",
+      "batchTimeMillis" : 1.0,
+      "batchMaxMessages" : 1000,
+      "batchMaxBytes" : 4194304,
+      "testTime" : 0,
+      "warmupTimeSeconds" : 1.0,
+      "encKeyName" : null,
+      "encKeyFile" : null,
+      "delay" : 0,
+      "exitOnFailure" : false,
+      "messageKeyGenerationMode" : null,
+      "producerAccessMode" : "Shared",
+      "formatPayload" : false,
+      "formatterClass" : "org.apache.pulsar.testclient.DefaultMessageFormatter",
+      "transactionTimeout" : 10,
+      "numMessagesPerTransaction" : 50,
+      "isEnableTransaction" : false,
+
+      "isAbortTransaction" : false,
+      "histogramFile" : null
+    }
+
+    ...
+
+    2023-03-08T15:53:28,178+0800 [Thread-0] INFO  org.apache.pulsar.testclient.PerformanceProducer - Aggregated latency stats --- Latency: mean:   4.481 ms - med:   2.918 - 95pct:  10.710 - 99pct:  38.928 - 99.9pct: 112.689 - 99.99pct: 154.241 - 99.999pct: 193.249 - Max: 241.717
+    ```
+
+**Step 4:** Check the internal stats of the topic _test-topic-2_.
+
+    **Input**
+
+    ```bash
+    curl -X GET http://localhost:8080/admin/v2/persistent/public/default/test-topic-2/partitioned-internalStats
+    ```
+
+    **Output**
+
+    For detailed explanations of topic stats, see Pulsar statistics.
+
+    ```bash
+    {"metadata":{"partitions":5},"partitions":{"persistent://public/default/test-topic-2-partition-3":{"entriesAddedCounter":47087,"numberOfEntries":47087,"totalSize":80406959,"currentLedgerEntries":47087,"currentLedgerSize":80406959,"lastLedgerCreatedTimestamp":"2023-03-08T15:47:07.273+08:00","waitingCursorsCount":0,"pendingAddEntriesCount":0,"lastConfirmedEntry":"117:47086","state":"LedgerOpened","ledgers":[{"ledgerId":117,"entries":0,"size":0,"offloaded":false,"underReplicated":false} [...]
+    ```
+
+**Step 5:** Delete the topic _test-topic-2_.
+
+    **Input**
+
+    ```
+    curl -X DELETE http://localhost:8080/admin/v2/persistent/public/default/test-topic-2/partitions
+    ```
+
+    **Output**
+
+    There is no output. You can verify whether the _test-topic-2_ exists or not using the following command.
+
+    **Input**
+
+    List topics in `public/default` namespace.
+
+    ```
+    curl -X GET http://localhost:8080/admin/v2/persistent/public/default
+    ```
+
+</TabItem>
+<TabItem value="Java">
+
+To manage topics using Java admin API, complete following steps.
+
+1. Initiate a Pulsar Java client.
+
+2. Create a partitioned topic
+
+3. Update the number of a partition.
+
+4. Produce messages to the topic.
+
+5. Check the stats of the topic.
+
+6. Delete the topic.
+
+**Prerequisites**
+
+- Prepare a Java project and add the following dependency to your POM file.
+
+  ```java
+  <dependency>
+        <groupId>org.apache.pulsar</groupId>
+        <artifactId>pulsar-client-admin</artifactId>
+        <version>@pulsar:version@</version>
+    </dependency>
+  ```
+
+**Steps**
+
+**Step 1:** Initiate a Pulsar Java client in your Java project.
+
+    **Input**
+
+    ```java
+    String url = "http://localhost:8080";
+    PulsarAdmin admin = PulsarAdmin.builder()
+        .serviceHttpUrl(url)
+        .build();
+    ```
+
+**Step 2:** Create a partitioned topic _test-topic-1_ with 4 partitions.
+
+    **Input**
+
+    ```java
+    admin.topics().createPartitionedTopic("persistent://public/default/test-topic-1", 4);
+    ```
+
+**Step 3:** Update the number of the partition to 5.
+
+    **Input**
+
+    ```java
+    admin.topics().updatePartitionedTopic("test-topic-1", 5);
+    ```
+
+**Step 4:** Produce some messages to the topic _test-topic-1_.
+
+    **Input**
+
+    ```java
+    PulsarClient client = PulsarClient.builder()
+        .serviceUrl("pulsar://localhost:6650")
+        .build();
+
+    Producer<String> producer = client.newProducer(Schema.STRING)
+        .topic("test-topic-1")
+        .blockIfQueueFull(true)
+        .create();
+
+    for (int i = 0; i < 100; ++i) {
+        producer.newMessage().value("test").send();
+    }
+    producer.close();
+    client.close();
+    ```
+
+**Step 5:** Check the stats of the topic _test-topic-1_.
+
+    **Input**
+
+    ```java
+    PartitionedTopicStats stats = admin.topics().getPartitionedStats("persistent://public/default/test-topic-1",false);
+    System.out.println(stats.getMsgInCounter());
+    ```
+    
+    **Output**
+
+    ```java
+    100
+    ```
+
+**Step 6:** Delete the topic _test-topic-1_.
+
+    **Input**
+
+    ```java
+    admin.topics().deletePartitionedTopic("test-topic-1");
+    ```
+
+</TabItem>
+
+</Tabs>
+
+## Related topics
+
+- To understand basics, see [Pulsar admin API - Overview](admin-api-overview.md)
+
+- To learn usage scenarios, see [Pulsar admin API - Use cases](admin-api-use-cases.md).
+
+- To learn common administrative tasks, see [Pulsar admin API - Features](admin-api-features.md).
+
+- To perform administrative operations, see [Pulsar admin API - Tools](admin-api-tools.md).
+
+- To check the detailed usage, see the references below.
+
+  - [pulsar-admin CLI](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/)
+
+  - Pulsar admin APIs
+
+    - [REST API](reference-rest-api-overview.md)
+
+    - [Java admin API](/api/admin/)
\ No newline at end of file
diff --git a/versioned_docs/version-4.0.x/administration-anti-affinity-namespaces.md b/versioned_docs/version-4.0.x/administration-anti-affinity-namespaces.md
new file mode 100644
index 000000000000..ad7753cc38ae
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-anti-affinity-namespaces.md
@@ -0,0 +1,59 @@
+---
+id: administration-anti-affinity-namespaces
+title: Anti-affinity namespaces
+sidebar_label: "Anti-affinity namespaces"
+description: Get a comprehensive understanding of anti-affinity namespaces in Pulsar.
+---
+
+## Distribute anti-affinity namespaces across failure domains
+
+When your application has multiple namespaces and you want one of them available all the time to avoid any downtime, you can group these namespaces and distribute them across different [failure domains](reference-terminology.md#failure-domain) and different brokers. Thus, if one of the failure domains is down (due to release rollout or brokers restart), it only disrupts namespaces owned by that specific failure domain and the rest of the namespaces owned by other domains remain available [...]
+
+Such a group of namespaces has anti-affinity to each other, that is, all the namespaces in this group are [anti-affinity namespaces](reference-terminology.md#anti-affinity-namespaces) and are distributed to different failure domains in a load-balanced manner.
+
+As illustrated in the following figure, Pulsar has 2 failure domains (Domain1 and Domain2) and each domain has 2 brokers in it. You can create an anti-affinity namespace group that has 4 namespaces in it, and all the 4 namespaces have anti-affinity to each other. The load manager tries to distribute namespaces evenly across all the brokers in the same domain. Since each domain has 2 brokers, every broker owns one namespace from this anti-affinity namespace group, and you can see each dom [...]
+
+![Distribute anti-affinity namespaces across failure domains](/assets/anti-affinity-namespaces-across-failure-domains.svg)
+
+The load manager follows an even distribution policy across failure domains to assign anti-affinity namespaces. The following table outlines the even-distributed assignment sequence illustrated in the above figure.
+
+| Assignment sequence | Namespace | Failure domain candidates | Broker candidates | Selected broker |
+|:---|:------------|:------------------|:------------------------------------|:-----------------|
+| 1 | Namespace1 | Domain1, Domain2 | Broker1, Broker2, Broker3, Broker4 | Domain1:Broker1 |
+| 2 | Namespace2 | Domain2          | Broker3, Broker4                   | Domain2:Broker3 |
+| 3 | Namespace3 | Domain1, Domain2 | Broker2, Broker4                   | Domain1:Broker2 |
+| 4 | Namespace4 | Domain2          | Broker4                            | Domain2:Broker4 |
+
+:::tip
+
+* Each namespace belongs to only one anti-affinity group. If a namespace with an existing anti-affinity assignment is assigned to another anti-affinity group, the original assignment is dropped.
+
+* If there are more anti-affinity namespaces than failure domains, the load manager distributes namespaces evenly across all the domains, and also every domain distributes namespaces evenly across all the brokers under that domain.
+
+:::
+
+### Create a failure domain and register brokers
+
+:::note
+
+One broker can only be registered to a single failure domain.
+
+:::
+
+To create a domain under a specific cluster and register brokers, run the following command:
+
+```bash
+pulsar-admin clusters create-failure-domain <cluster-name> --domain-name <domain-name> --broker-list <broker-list-comma-separated>
+```
+
+You can also view, update, and delete domains under a specific cluster. For more information, refer to [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+### Create an anti-affinity namespace group
+
+An anti-affinity group is created automatically when the first namespace is assigned to the group. To assign a namespace to an anti-affinity group, run the following command. It sets an anti-affinity group name for a namespace.
+
+```bash
+pulsar-admin namespaces set-anti-affinity-group <namespace> --group <group-name>
+```
+
+For more information about `anti-affinity-group` related commands, refer to [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
diff --git a/versioned_docs/version-4.0.x/administration-geo.md b/versioned_docs/version-4.0.x/administration-geo.md
new file mode 100644
index 000000000000..edef835d1fce
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-geo.md
@@ -0,0 +1,286 @@
+---
+id: administration-geo
+title: Pulsar geo-replication
+sidebar_label: "Geo-replication"
+description: Get a comprehensive understanding of geo-replication in Pulsar.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+## Enable geo-replication for a namespace
+
+You must enable geo-replication on a [per-tenant basis](#concepts-multi-tenancy) in Pulsar. For example, you can enable geo-replication between two specific clusters only when a tenant has access to both clusters.
+
+Geo-replication is managed at the namespace level, which means you only need to create and configure a namespace to replicate messages between two or more provisioned clusters that a tenant can access.
+
+Complete the following tasks to enable geo-replication for a namespace:
+
+* [Enable a geo-replication namespace](#enable-geo-replication-at-namespace-level)
+* [Configure that namespace to replicate across two or more provisioned clusters](admin-api-namespaces.md#configure-replication-clusters)
+
+Any message published on *any* topic in that namespace is replicated to all clusters in the specified set.
+
+## Local persistence and forwarding
+
+When messages are produced on a Pulsar topic, messages are first persisted in the local cluster, and then forwarded asynchronously to the remote clusters.
+
+In normal cases, when connectivity issues are none, messages are replicated immediately, at the same time as they are dispatched to local consumers. Typically, the network [round-trip time](https://en.wikipedia.org/wiki/Round-trip_delay_time) (RTT) between the remote regions defines end-to-end delivery latency.
+
+Applications can create producers and consumers in any of the clusters, even when the remote clusters are not reachable (like during a network partition).
+
+Producers and consumers can publish messages to and consume messages from any cluster in a Pulsar instance. However, subscriptions cannot only be local to the cluster where the subscriptions are created but also can be transferred between clusters after the replicated subscription is enabled. Once the replicated subscription is enabled, you can keep the subscription state in synchronization. Therefore, a topic can be asynchronously replicated across multiple geographical regions. In case [...]
+
+![Geo-replication example with a full-mesh pattern](/assets/geo-replication.png)
+
+In the aforementioned example, the **T1** topic is replicated among three clusters, **Cluster-A**, **Cluster-B**, and **Cluster-C**.
+
+All messages produced in any of the three clusters are delivered to all subscriptions in other clusters. In this case, **C1** and **C2** consumers receive all messages that **P1**, **P2**, and **P3** producers publish. Ordering is still guaranteed on a per-producer basis.
+
+## Configure replication
+
+To configure geo-replicated clusters, complete the following steps.
+
+### Step 1: Connect replication clusters
+
+To replicate data among clusters, you need to configure each cluster to connect to the other. You can use the [`pulsar-admin`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/) tool to create a connection.
+
+**Example**
+
+Suppose that you have 3 replication clusters: `us-west`, `us-cent`, and `us-east`.
+
+1. Configure the connection from `us-west` to `us-east`.
+
+   Run the following command on `us-west`.
+
+   ```shell
+   bin/pulsar-admin clusters create \
+   --broker-url pulsar://<DNS-OF-US-EAST>:<PORT> \
+   --url http://<DNS-OF-US-EAST>:<PORT> \
+   us-east
+   ```
+
+:::tip
+
+   - If you want to use a secure connection for a cluster, you can use the flags `--broker-url-secure` and `--url-secure`. For more information, see [pulsar-admin clusters create](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/clusters?id=create).
+   - Different clusters may have different authentications. You can use the authentication flag `--auth-plugin` and `--auth-parameters` together to set cluster authentication, which overrides `brokerClientAuthenticationPlugin` and `brokerClientAuthenticationParameters` if `authenticationEnabled` sets to `true` in `broker.conf` and `standalone.conf`. For more information, see [authentication and authorization](concepts-authentication.md).
+
+:::
+
+2. Configure the connection from `us-west` to `us-cent`.
+
+   Run the following command on `us-west`.
+
+   ```shell
+   bin/pulsar-admin clusters create \
+   --broker-url pulsar://<DNS-OF-US-CENT>:<PORT>	\
+   --url http://<DNS-OF-US-CENT>:<PORT> \
+   us-cent
+   ```
+
+3. Run similar commands on `us-east` and `us-cent` to create connections among clusters.
+
+### Step 2: Grant permissions to properties
+
+To replicate to a cluster, the tenant needs permission to use that cluster. You can grant permission to the tenant when you create the tenant or grant it later.
+
+Specify all the intended clusters when you create a tenant:
+
+```shell
+bin/pulsar-admin tenants create my-tenant \
+--admin-roles my-admin-role \
+--allowed-clusters us-west,us-east,us-cent
+```
+
+To update permissions of an existing tenant, use `update` instead of `create`.
+
+### Step 3: Enable geo-replication
+
+You can enable geo-replication at **namespace** or **topic** level.
+
+#### Enable geo-replication at namespace level
+
+You can create a namespace with the following command sample.
+
+```shell
+bin/pulsar-admin namespaces create my-tenant/my-namespace
+```
+
+Initially, the namespace is not assigned to any cluster. You can assign the namespace to clusters using the `set-clusters` subcommand:
+
+```shell
+bin/pulsar-admin namespaces set-clusters my-tenant/my-namespace \
+--clusters us-west,us-east,us-cent
+```
+
+#### Enable geo-replication at topic level
+
+You can set geo-replication at topic level using the command `pulsar-admin topics set-replication-clusters`. For the latest and complete information about `Pulsar admin`, including commands, flags, descriptions, and more information, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+```shell
+bin/pulsar-admin topics set-replication-clusters --clusters us-west,us-east,us-cent my-tenant/my-namespace/my-topic
+```
+
+:::tip
+
+- You can change the replication clusters for a namespace at any time, without disruption to ongoing traffic. Replication channels are immediately set up or stopped in all clusters as soon as the configuration changes.
+- Once you create a geo-replication namespace, any topics that producers or consumers create within that namespace are replicated across clusters. Typically, each application uses the `serviceUrl` for the local cluster.
+- If you are using Pulsar version `2.10.x`, to enable geo-replication at topic level, you need to change the following configurations in the `conf/broker.conf` or `conf/standalone.conf` file to enable topic policies service.
+
+```conf
+systemTopicEnabled=true
+topicLevelPoliciesEnabled=true
+```
+
+:::
+
+### Step 4: Use topics with geo-replication
+
+#### Selective replication
+
+By default, messages are replicated to all clusters configured for the namespace. You can restrict replication selectively by specifying a replication list for a message, and then that message is replicated only to the subset in the replication list.
+
+The following is an example of the [Java API](client-libraries-java.md). Note the use of the `replicationClusters` method when you construct the [Message](/api/client/org/apache/pulsar/client/api/Message) object:
+
+```java
+List<String> restrictReplicationTo = Arrays.asList(
+        "us-west",
+        "us-east"
+);
+
+Producer producer = client.newProducer()
+        .topic("some-topic")
+        .create();
+
+producer.newMessage()
+        .value("my-payload".getBytes())
+        .replicationClusters(restrictReplicationTo)
+        .send();
+```
+
+#### Topic stats
+
+You can check topic-specific statistics for geo-replication topics using one of the following methods.
+
+````mdx-code-block
+<Tabs groupId="api-choice"
+  defaultValue="pulsar-admin"
+  values={[{"label":"pulsar-admin","value":"pulsar-admin"},{"label":"REST API","value":"REST API"}]}>
+<TabItem value="pulsar-admin">
+
+Use the [`pulsar-admin topics stats`](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/topics?id=stats) command.
+
+```shell
+bin/pulsar-admin topics stats persistent://my-tenant/my-namespace/my-topic
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|GET|/admin/v2/:schema/:tenant/:namespace/:topic/stats|operation/getStats?version=@pulsar:version_number@}
+
+</TabItem>
+
+</Tabs>
+````
+
+Each cluster reports its own local stats, including the incoming and outgoing replication rates and backlogs.
+
+#### Delete a geo-replication topic
+
+Given that geo-replication topics exist in multiple regions, directly deleting a geo-replication topic is not possible. Instead, you should rely on automatic topic garbage collection.
+
+In Pulsar, a topic is automatically deleted when the topic meets the following three conditions:
+- no producers or consumers are connected to it;
+- no subscriptions to it;
+- no more messages are kept for retention.
+For geo-replication topics, each region uses a fault-tolerant mechanism to decide when deleting the topic locally is safe.
+
+You can explicitly disable topic garbage collection by setting `brokerDeleteInactiveTopicsEnabled` to `false` in your [broker configuration](reference-configuration.md#broker).
+
+To delete a geo-replication topic, close all producers and consumers on the topic, and delete all of its local subscriptions in every replication cluster. When Pulsar determines that no valid subscription for the topic remains across the system, it will garbage collect the topic.
+
+## Replicated subscriptions
+
+Pulsar supports replicated subscriptions, so you can keep the subscription state in sync, within a sub-second timeframe, in the context of a topic that is being asynchronously replicated across multiple geographical regions.
+
+In case of failover, a consumer can restart consuming from the failure point in a different cluster.
+
+### Enable replicated subscription
+
+If you want to use replicated subscriptions in Pulsar:
+
+- For broker side: set `enableReplicatedSubscriptions` to `true` in [`broker.conf`](https://github.com/apache/pulsar/blob/470b674016c8718f2dfd0a0f93cf02d49af0fead/conf/broker.conf#L592).
+
+- For consumer side: replicated subscription is disabled by default. You can enable replicated subscriptions when creating a consumer.
+
+  ```java
+  Consumer<String> consumer = client.newConsumer(Schema.STRING)
+              .topic("my-topic")
+              .subscriptionName("my-subscription")
+              .replicateSubscriptionState(true)
+              .subscribe();
+  ```
+
+### Advantages
+
+The advantages of replicated subscription are as follows.
+
+ * It is easy to implement the logic.
+ * You can choose to enable or disable replicated subscription.
+ * When you enable it, the overhead is low, and it is easy to configure.
+ * When you disable it, the overhead is zero.
+
+### Limitations
+
+The limitations of replicated subscription are as follows.
+
+* When you enable replicated subscriptions, you're creating a consistent distributed snapshot to establish an association between message ids from different clusters. The snapshots are taken periodically. The default value is `1 second`. It means that a consumer failing over to a different cluster can potentially receive 1 second of duplicates. You can also configure the frequency of the snapshot in the `broker.conf` file.
+* Only the base line cursor position is synced in replicated subscriptions while the individual acknowledgments are not synced. This means the messages acknowledged out-of-order could end up getting delivered again, in the case of a cluster failover.
+
+:::note
+
+* This replicated subscription will add a new special message every second, it will contains the [snapshot](https://github.com/apache/pulsar/wiki/PIP-33:-Replicated-subscriptions#storing-snapshots) of the subscription. That means,  if there are inactive subscriptions over the topic there can be an increase in backlog in source and destination clusters.
+
+:::
+
+## Migrate data between clusters using geo-replication
+
+Using geo-replication to migrate data between clusters is a special use case of the [active-active replication pattern](concepts-replication.md#active-active-replication) when you don't have a large amount of data.
+
+1. Create your new cluster.
+2. Add the new cluster to your old cluster.
+
+   ```shell
+   bin/pulsar-admin clusters create new-cluster
+   ```
+
+3. Add the new cluster to your tenant.
+
+   ```shell
+   bin/pulsar-admin tenants update my-tenant --cluster old-cluster,new-cluster
+   ```
+
+4. Set the clusters on your namespace.
+
+   ```shell
+   bin/pulsar-admin namespaces set-clusters my-tenant/my-ns --cluster old-cluster,new-cluster
+   ```
+
+5. Update your applications using [replicated subscriptions](#replicated-subscriptions).
+6. Validate subscription replication is active.
+   ```shell
+   bin/pulsar-admin topics stats-internal public/default/t1
+   ```
+
+7. Move your consumers and producers to the new cluster by modifying the values of `serviceURL`.
+
+:::note
+
+* The replication starts from step 4, which means existing messages in your old cluster are not replicated.
+* If you have some older messages to migrate, you can pre-create the replication subscriptions for each topic and set it at the earliest position by using `pulsar-admin topics create-subscription -s pulsar.repl.new-cluster -m earliest <topic>`. Until [PIP-356](https://github.com/apache/pulsar/blob/master/pip/pip-356.md) is merged you will need to unload the topic to start georeplication.
+
+:::
diff --git a/versioned_docs/version-4.0.x/administration-isolation-bookie.md b/versioned_docs/version-4.0.x/administration-isolation-bookie.md
new file mode 100644
index 000000000000..04fd94345d47
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-isolation-bookie.md
@@ -0,0 +1,270 @@
+---
+id: administration-isolation-bookie
+title: Isolate bookies
+sidebar_label: "Isolate bookies"
+description: Get a comprehensive understanding of bookie data isolation policy in Pulsar.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+Isolating bookies equals isolating message storage, which is a data storage mechanism that provides isolation and safety for specific topics.
+
+Bookie isolation is controlled by BookKeeper clients. For Pulsar, there are two kinds of BookKeeper clients to read and write data.
+*  BookKeeper clients on the broker side: Pulsar brokers use these BookKeeper clients to read and write topic messages.
+*  BookKeeper clients on the bookie auto-recovery side:
+   * The bookie auditor checks whether ledger replicas fulfill the configured isolation policy;
+   * The bookie replication worker writes ledger replicas to target bookies according to the configured isolation policy.
+
+To isolate bookies, you need to complete the following tasks.
+1. Select a [data isolation policy](#understand-bookie-data-isolation-policy) based on your requirements.
+2. [Enable the policy on BookKeeper clients](#enable-bookie-data-placement-policy).
+3. [Configure the policy on bookie instances](#configure-data-placement-policy-on-bookie-instances).
+
+
+## Understand bookie data isolation policy
+
+Bookie data isolation policy is built on top of the existing BookKeeper rack-aware placement policy. The "rack" concept can be anything, for example, racks, regions, availability zones. It writes the configured isolation policy into the metadata store. Both BookKeeper clients on the broker and bookie auto-recovery side read the configured isolation policy from the metadata store and apply it when choosing bookies to store messages.
+
+BookKeeper provides three kinds of data isolation policies for disaster tolerance.
+* Rack-aware placement policy (default)
+* Region-aware placement policy
+* Zone-aware placement policy
+
+:::tip
+
+* Both [rack-aware placement policy](#rack-aware-placement-policy) and [region-aware placement policy](#region-aware-placement-policy) can be used in all kinds of deployments where racks are a subset of a region. The major difference between the two policies is:
+  * With `RackawareEnsemblePlacementPolicy` configured, the BookKeeper client chooses bookies from different **racks** to reduce the single point of failure. If there is only one rack available, the policy falls back on choosing a random bookie across available ones.
+  * With `RegionAwareEnsemblePlacementPolicy` configured, the BookKeeper client chooses bookies from different **regions**; for the selected region, it chooses bookies from different racks if more than one ensemble falls into the same region.
+
+* Zone-aware placement policy (`ZoneAwareEnsemblePlacementPolicy`) can be used in a public cloud infrastructure where Availability Zones (AZs) are isolated locations within the data center regions that public cloud services originate from and operate in.
+
+:::
+
+### Rack-aware placement policy
+
+Rack-aware placement policy enforces different data replicas to be placed in different racks to guarantee the rack-level disaster tolerance for your production environment. A data center usually has a lot of racks, and each rack has many storage nodes. You can use `RackAwareEnsemblePlacementPolicy` to configure the rack information for each bookie.
+
+#### Qualified rack size of bookies
+
+When the available rack size of bookies can meet the requirements configured on a topic, the rack-aware placement policy can work well and you don't need any extra configurations.
+
+For example, the BookKeeper cluster has 4 racks and 13 bookie instances as shown in the following diagram. When a topic is configured with `EnsembleSize=3, WriteQuorum=3, AckQuorum=2`, the BookKeeper client chooses one bookie instance from three different racks to write data to, such as Bookie2, Bookie8, and Bookie12.
+
+
+![Rack-aware placement policy in Pulsar](/assets/rack-aware-placement-policy-1.svg)
+
+#### Enforced minimum rack size of bookies
+
+When the available rack size of bookies cannot meet the requirements configured on a topic, the strategy that the BookKeeper client chooses bookies to recover old ledgers and create new ledgers depends on whether the enforced minimum rack size of bookies is configured.
+
+In this case, if you want to make the rack-aware placement policy work as usual, you need to configure an enforced minimum rack size of bookies (`MinNumRacksPerWriteQuorum`).
+
+For example, you have the same BookKeeper cluster with the same topic requirements `EnsembleSize=3, WriteQuorum=3, AckQuorum=2` as shown in the above diagram. When all the bookie instances in Rack3 and Rack4 fail, you only have 2 available racks and there are the following three possibilities.
+
+* If you have configured `EnforceMinNumRacksPerWriteQuorum=true` and `MinNumRacksPerWriteQuorum=3`, the BookKeeper client fails to choose bookies, which means new ledgers cannot be created and old ledgers cannot be recovered. Because the requirement of `MinNumRacksPerWriteQuorum=3` cannot be fulfilled.
+
+* If you have configured `EnforceMinNumRacksPerWriteQuorum=true` and `MinNumRacksPerWriteQuorum=2`, the BookKeeper client chooses one bookie from Rack1 and Rack2 to recover old ledgers, such as bookie1 and bookie5, to place 2 replicas for Bookie8 and Bookie12. For new ledger creation, it chooses one bookie from Rack1 and Rack2, such as Bookie4 and Bookie7, and a random bookie from either Rack1 or Rack2 to place the last replica.
+
+![Rack-aware placement policy with an enforced minimum rack size of bookies](/assets/rack-aware-placement-policy-2.svg)
+
+* If you have configured `EnforceMinNumRacksPerWriteQuorum=false`, the BookKeeper client tries its best effort to apply the placement policy depending on the available number of racks and bookies. It may still work as the above diagram or the following diagram.
+
+![Rack-aware placement policy without an enforced minimum rack size of bookies](/assets/rack-aware-placement-policy-3.svg)
+
+### Region-aware placement policy
+
+Region-aware placement policy enforces different data replicas to be placed in different regions and racks to guarantee region-level disaster tolerance. To achieve datacenter level disaster tolerance, you need to write data replicas into different data centers. You can use `RegionAwareEnsemblePlacementPolicy` to configure region and rack information for each bookie node to ensure region-level disaster tolerance.
+
+For example, the BookKeeper cluster has 4 regions, and each region has several racks with their bookie instances, as shown in the following diagram. If a topic is configured with `EnsembleSize=3, WriteQuorum=3, and AckQuorum=2`, the BookKeeper client chooses three different regions, such as Region A, Region C and Region D. For each region, it chooses one bookie on a single rack, such as Bookie5 on Rack2, Bookie17 on Rack6, and Bookie21 on Rack8.
+
+![Region-aware placement policy in Pulsar](/assets/region-aware-placement-policy-1.svg)
+
+When two regions fail, such as Region B and Region C, as shown in the following diagram, the BookKeeper client chooses one bookie from Region A or Region D to replace the failed Bookie17 for recovering old ledgers. And it also chooses Region A and Region D to write replicas for creating new ledgers. In Region A, it falls back to rack-aware placement policy and chooses one bookie from Rack1 and Rack2, such as Bookie4 and Bookie7. For Region D, it has to choose one bookie from Rack8, such  [...]
+
+![Region-aware placement policy for disaster tolerance](/assets/region-aware-placement-policy-2.svg)
+
+## Enable bookie data placement policy
+
+By default, the rack-aware placement policy is enabled on both broker and bookie sides. If you want to switch to the region-aware placement policy, you need to enable the region-aware placement policy on both broker and bookie sides.
+
+### Enable region-aware placement policy on broker
+
+To enable region-aware placement policy on broker, configure the following field in the `conf/broker.conf` file.
+
+```properties
+bookkeeperClientRegionawarePolicyEnabled=true
+```
+
+To enforce the minimum rack size of bookies, configure the following fields:
+
+```properties
+bookkeeperClientEnforceMinNumRacksPerWriteQuorum=true
+bookkeeperClientMinNumRacksPerWriteQuorum=2
+```
+
+To balance the ledger disk usage of different bookies, you can enable the disk weight placement by configuring the following field:
+
+```properties
+bookkeeperDiskWeightBasedPlacementEnabled=true
+```
+### Enable region-aware placement policy on the auto-recovery instances (pods)
+
+To enable region-aware placement policy on the auto-recovery instances, configure the following fields in the `conf/bookkeeper.conf` file.
+
+```properties
+ensemblePlacementPolicy=org.apache.bookkeeper.client.RegionAwareEnsemblePlacementPolicy
+reppDnsResolverClass=org.apache.pulsar.zookeeper.ZkBookieRackAffinityMapping
+```
+
+To enforce the minimum rack size of bookies, configure the following fields:
+
+```properties
+enforceMinNumRacksPerWriteQuorum=true
+minNumRacksPerWriteQuorum=2
+```
+
+To balance the ledger disk usage of different bookies, you can enable the disk weight placement by configuring the following field:
+
+```properties
+diskWeightBasedPlacementEnabled=true
+```
+
+## Configure data placement policy on bookie instances
+
+To configure a data placement policy on bookie instances, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs
+  defaultValue="Pulsar-admin CLI"
+  values={[{"label":"Pulsar-admin CLI","value":"Pulsar-admin CLI"},{"label":"REST API","value":"REST API"}]}>
+
+<TabItem value="Pulsar-admin CLI">
+
+
+Specify the rack name to represent which region or rack this bookie belongs to.
+
+```bash
+bin/pulsar-admin bookies set-bookie-rack
+The following options are required: [-b | --bookie], [-r | --rack]
+
+Then we need to update the rack placement information for a specific bookie in the cluster. Note that the bookie address format is `address:port`.
+Usage: set-bookie-rack [options]
+  Options:
+  * -b, --bookie
+      Bookie address (format: `address:port`)
+    -g, --group
+      Bookie group name
+      Default: default
+    --hostname
+      Bookie host name
+  * -r, --rack
+      Bookie rack name
+```
+
+:::tip
+
+In addition, you can also group bookies across racks or regions to serve broker-level isolation by specifying a group name for each bookie and assigning the group name to a specific namespace. See [configure bookie affinity groups](#configure-bookie-affinity-groups) for more details.
+
+:::
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/bookies/racks-info/:bookie|operation/updateBookieRackInfo?version=@pulsar:version_number@}
+
+</TabItem>
+
+</Tabs>
+````
+
+
+#### Example of configuring rack-aware placement policy
+
+The following is an example of how to configure bookie instances with their rack properties.
+
+```bash
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie1:3181 --hostname bookie1.pulsar.com:3181 --group group1 --rack rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie2:3181 --hostname bookie2.pulsar.com:3181 --group group1 --rack rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie3:3181 --hostname bookie3.pulsar.com:3181 --group group1 --rack rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie4:3181 --hostname bookie4.pulsar.com:3181 --group group1 --rack rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie5:3181 --hostname bookie5.pulsar.com:3181 --group group1 --rack rack2
+...
+```
+
+#### Example of configuring region-aware placement policy
+
+The following is an example of how to configure bookie instances with their region/rack properties.
+
+```bash
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie1:3181 --hostname bookie1.pulsar.com:3181 --group group1 --rack RegionA/rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie2:3181 --hostname bookie2.pulsar.com:3181 --group group1 --rack RegionA/rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie3:3181 --hostname bookie3.pulsar.com:3181 --group group1 --rack RegionA/rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie4:3181 --hostname bookie4.pulsar.com:3181 --group group1 --rack RegionA/rack1
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie5:3181 --hostname bookie5.pulsar.com:3181 --group group1 --rack RegionA/rack2
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie6:3181 --hostname bookie6.pulsar.com:3181 --group group1 --rack RegionA/rack2
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie7:3181 --hostname bookie7.pulsar.com:3181 --group group1 --rack RegionA/rack2
+bin/pulsar-admin bookies set-bookie-rack --bookie bookie8:3181 --hostname bookie8.pulsar.com:3181 --group group1 --rack RegionB/rack3
+...
+```
+
+## Configure bookie affinity groups
+
+The data of a namespace can be isolated into user-defined groups of bookies, as known as bookie affinity groups, which guarantee all the data that belongs to the namespace is stored in desired bookies.
+
+**Prerequisites:** Before configuring bookie affinity groups, you need to group bookies first. See [configure data placement policy on bookie instances](#configure-data-placement-policy-on-bookie-instances) for more details.
+
+To configure bookie affinity groups, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs
+  defaultValue="Pulsar-admin CLI"
+  values={[{"label":"Pulsar-admin CLI","value":"Pulsar-admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java admin API","value":"Java admin API"}]}>
+
+<TabItem value="Pulsar-admin CLI">
+
+```shell
+pulsar-admin namespaces set-bookie-affinity-group options
+```
+
+For more information about the command `pulsar-admin namespaces set-bookie-affinity-group options`, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+**Example**
+
+```shell
+bin/pulsar-admin bookies set-bookie-rack \
+--bookie 127.0.0.1:3181 \
+--hostname 127.0.0.1:3181 \
+--group group-bookie1 \
+--rack rack1
+
+bin/pulsar-admin namespaces set-bookie-affinity-group public/default \
+--primary-group group-bookie1
+```
+
+:::note
+
+- Do not set a bookie rack name to slash (`/`) or an empty string (`""`) if you use Pulsar earlier than 2.7.5, 2.8.3, and 2.9.2. If you use Pulsar 2.7.5, 2.8.3, 2.9.2 or later versions, it falls back to `/default-rack` or `/default-region/default-rack`.
+- When `RackawareEnsemblePlacementPolicy` is enabled, the rack name is not allowed to contain one slash (`/`) except for the beginning and end of the rack name string. For example, a rack name like `/rack0` is allowed, but `/rack/0` is invalid.
+- When `RegionAwareEnsemblePlacementPolicy` is enabled, the rack name can only contain one slash (`/`) except for the beginning and end of the rack name string. For example, rack name like `/region0/rack0` is allowed, but `/region0rack0` and `/region0/rack/0` are invalid.
+For the bookie rack name restrictions, see [pulsar-admin bookies set-bookie-rack](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/bookies?id=set-bookie-rack).
+
+:::
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|POST|/admin/v2/namespaces/:tenant/:namespace/persistence/bookieAffinity|operation/setBookieAffinityGroup?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java admin API">
+
+For how to set bookie affinity group for a namespace using Java admin API, see [code](https://github.com/apache/pulsar/blob/master/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NamespacesImpl.java#L1164).
+
+</TabItem>
+
+</Tabs>
+````
diff --git a/versioned_docs/version-4.0.x/administration-isolation-broker.md b/versioned_docs/version-4.0.x/administration-isolation-broker.md
new file mode 100644
index 000000000000..54a8f2920e9b
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-isolation-broker.md
@@ -0,0 +1,61 @@
+---
+id: administration-isolation-broker
+title: Isolate brokers
+sidebar_label: "Isolate brokers"
+description: Learn to set a namespace isolation policy for a broker cluster in Pulsar.
+---
+
+````mdx-code-block
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+````
+
+
+In Pulsar, when namespaces (more specifically, namespace bundles) are assigned dynamically to brokers, the namespace isolation policy limits the set of brokers that can be used for assignment. Before topics are assigned to brokers, you can set the namespace isolation policy with a primary or a secondary regex to select desired brokers.
+
+To set a namespace isolation policy for a broker cluster, you can use one of the following methods.
+
+````mdx-code-block
+<Tabs
+  defaultValue="Pulsar-admin CLI"
+  values={[{"label":"Pulsar-admin CLI","value":"Pulsar-admin CLI"},{"label":"REST API","value":"REST API"},{"label":"Java admin API","value":"Java admin API"}]}>
+
+<TabItem value="Pulsar-admin CLI">
+
+```shell
+pulsar-admin ns-isolation-policy set options
+```
+
+For more information about the command `pulsar-admin ns-isolation-policy set options`, see [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+**Example**
+
+```shell
+bin/pulsar-admin ns-isolation-policy set \
+--auto-failover-policy-type min_available \
+--auto-failover-policy-params min_limit=1,usage_threshold=80 \
+--namespaces my-tenant/my-namespace \
+--primary 10.193.216.*  my-cluster policy-name
+```
+
+</TabItem>
+<TabItem value="REST API">
+
+{@inject: endpoint|PUT|/admin/v2/:namespace/:tenant/:namespace|operation/createNamespace?version=@pulsar:version_number@}
+
+</TabItem>
+<TabItem value="Java admin API">
+
+For how to set namespace isolation policy using Java admin API, see [code](https://github.com/apache/pulsar/blob/master/pulsar-client-admin/src/main/java/org/apache/pulsar/client/admin/internal/NamespacesImpl.java#L251).
+
+</TabItem>
+
+</Tabs>
+````
+
+
+:::tip
+
+To guarantee all the data that belongs to a namespace is stored in desired bookies, you can isolate the data of the namespace into user-defined groups of bookies. See [configure bookie affinity groups](administration-isolation-bookie.md#configure-bookie-affinity-groups) for more details.
+
+:::
diff --git a/versioned_docs/version-4.0.x/administration-isolation.md b/versioned_docs/version-4.0.x/administration-isolation.md
new file mode 100644
index 000000000000..0d20cc116199
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-isolation.md
@@ -0,0 +1,74 @@
+---
+id: administration-isolation
+title: Pulsar isolation
+sidebar_label: "Pulsar isolation"
+description: Get a comprehensive understanding of Pulsar isolation.
+---
+
+
+In an organization, a Pulsar instance provides services to multiple teams. When organizing the resources across multiple teams, you want to make a suitable isolation plan to avoid resource competition between different teams and applications and provide high-quality messaging service. In this case, you need to consider resource isolation and weigh your intended actions against expected and unexpected consequences.
+
+The multi-layer and segment-centric architecture and hierarchical resource management of Pulsar provide a solid foundation for isolation, which allows you to isolate resources in your desired manner, prevent resource competition, and attain stability.
+
+
+## Isolation levels
+
+Pulsar supports isolation at either of the following two levels or both.
+* [Broker-level isolation](administration-isolation-broker.md) divides brokers into different groups and assigns broker groups to different namespaces. In this way, you can bind topics in a namespace to a set of brokers that belong to specific groups.
+* [Bookie-level isolation](administration-isolation-bookie.md) divides bookies into different racks/regions and assigns data replicas to bookies based on a specified data placement policy for disaster tolerance.
+
+![Broker-level isolation and bookie-level isolation in Pulsar](/assets/admin-isolation.svg)
+
+:::tip
+
+On top of [broker-level isolation](administration-isolation-broker.md) and [bookie-level isolation](administration-isolation-bookie.md), if you want to guarantee all the data that belongs to a namespace is stored in desired bookies, you can define and configure [bookie affinity groups](administration-isolation-bookie.md#configure-bookie-affinity-groups). See [shared BookKeeper cluster deployment](#shared-bookkeeper-cluster) for more details.
+
+:::
+
+
+## Deployments to achieve isolation within Pulsar
+
+### Separate Pulsar clusters
+
+The following illustration demonstrates the deployment of separate Pulsar clusters to achieve the highest-level isolation.
+
+![Deployment of separate Pulsar clusters](/assets/isolation-1.png)
+
+Here are some key points for understanding how it works:
+- Separate Pulsar clusters use a shared [configuration store](concepts-architecture-overview.md#configuration-store).
+- Each cluster exposes its service through a DNS entry point and makes sure a client can access the cluster through the DNS entry point. Clients can use one or multiple Pulsar URLs that the Pulsar cluster exposes as the service URL.
+- Each Pulsar cluster has one or multiple brokers and bookies.
+- Each Pulsar cluster has one metadata store, which can be separated into [Pulsar metadata store](concepts-architecture-overview.md#metadata-store) and [BookKeeper metadata store](https://bookkeeper.apache.org/docs/latest/getting-started/concepts/#metadata-storage).
+
+:::note
+
+When using this approach, if you want to achieve namespace isolation, you need to specify a cluster for a namespace. The cluster must be in the allowed cluster list of the tenant. Topics under the namespace are assigned to this cluster.
+
+:::
+
+### Shared BookKeeper cluster
+
+The following illustration demonstrates the deployment of shared BookKeeper clusters to achieve isolation.
+
+![Deployment of shared BookKeeper cluster](/assets/isolation-2.png)
+
+Here are some key points for understanding how it works:
+- Separate Pulsar clusters share a BookKeeper cluster and a [configuration store](concepts-architecture-overview.md#configuration-store).
+- Each cluster exposes its service through a DNS entry point and makes sure a client can access the cluster through the DNS entry point. Clients can use one or multiple Pulsar URLs that the Pulsar cluster exposes as the service URL.
+- Each Pulsar cluster has one or multiple brokers.
+- Each Pulsar cluster has one metadata store.
+
+As illustrated below, all bookie groups use a shared BookKeeper cluster and a metadata store, and each [bookie affinity group](administration-isolation-bookie.md#configure-bookie-affinity-groups) has one or several bookies. You can specify one or multiple primary/secondary groups for a namespace. Topics under the namespace are created on the bookies in the primary group first and then created on the bookies in the secondary group.
+
+![Storage isolation achieved by bookie affinity groups](/assets/isolation-3.png)
+
+### Single Pulsar cluster
+
+The following illustration demonstrates how to achieve isolation inside a single Pulsar cluster.
+
+![Deployment of a single Pulsar cluster](/assets/isolation-4.png)
+
+Here are some key points for understanding how it works:
+- Each cluster exposes its service through a DNS entry point and makes sure a client can access the cluster through the DNS entry point. Clients can use one or multiple Pulsar URLs that the Pulsar cluster exposes as the service URL.
+- Broker isolation is achieved by setting a [namespace isolation policy](administration-isolation-broker.md).
+
diff --git a/versioned_docs/version-4.0.x/administration-load-balance.md b/versioned_docs/version-4.0.x/administration-load-balance.md
new file mode 100644
index 000000000000..5640f63ef484
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-load-balance.md
@@ -0,0 +1,285 @@
+---
+id: administration-load-balance
+title: Load balance across brokers
+sidebar_label: "Load balance"
+---
+
+
+Pulsar is a horizontally scalable messaging system, so the traffic in a logical cluster must be balanced across all the available Pulsar brokers as evenly as possible, which is a core requirement.
+
+You can use multiple settings and tools to control the traffic distribution which requires a bit of context to understand how the traffic is managed in Pulsar. Though in most cases, the core requirement mentioned above is true out of the box and you should not worry about it.
+
+The following sections introduce how load-balanced assignments work across Pulsar brokers and how you can leverage the framework to adjust.
+
+## Dynamic assignments
+
+Topics are dynamically assigned to brokers based on the load conditions of all brokers in the cluster. The assignment of topics to brokers is not done at the topic level but the **bundle** level (a higher level). Instead of individual topic assignments, each broker takes ownership of a subset of the topics for a namespace. This subset is called a bundle and effectively this subset is a sharding mechanism.
+
+In other words, each namespace is an "administrative" unit and sharded into a list of bundles, with each bundle comprising a portion of the overall hash range of the namespace. Topics are assigned to a particular bundle by taking the hash of the topic name and checking in which bundle the hash falls. Each bundle is independent of the others and thus is independently assigned to different brokers.
+
+The benefit of the assignment granularity is to amortize the amount of information that you need to keep track of. Based on CPU, memory, traffic load, and other indexes, topics are assigned to a particular broker dynamically. For example:
+* When a client starts using new topics that are not assigned to any broker, a process is triggered to choose the best-suited broker to acquire ownership of these topics according to the load conditions.
+* If the broker owning a topic becomes overloaded, the topic is reassigned to a less-loaded broker.
+* If the broker owning a topic crashes, the topic is reassigned to another active broker.
+
+:::tip
+
+For partitioned topics, different partitions are assigned to different brokers. Here "topic" means either a non-partitioned topic or one partition of a topic.
+
+:::
+
+## Create namespaces with assigned bundles
+
+When you create a new namespace, a number of bundles are assigned to the namespace. You can set this number in the `conf/broker.conf` file:
+
+```conf
+# When a namespace is created without specifying the number of bundles, this
+# value will be used as the default
+defaultNumberOfNamespaceBundles=4
+```
+
+Alternatively, you can override the value when you create a new namespace using [Pulsar admin](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/):
+
+```shell
+bin/pulsar-admin namespaces create my-tenant/my-namespace --clusters us-west --bundles 16
+```
+
+With the above command, you create a namespace with 16 initial bundles. Therefore the topics for this namespace can immediately be spread across up to 16 brokers.
+
+In general, if you know the expected traffic and number of topics in advance, you had better start with a reasonable number of bundles instead of waiting for the system to auto-correct the distribution.
+
+On the same note, it is beneficial to start with more bundles than the number of brokers, due to the hashing nature of the distribution of topics into bundles. For example, for a namespace with 1000 topics, using something like 64 bundles achieves a good distribution of traffic across 16 brokers.
+
+
+## Split namespace bundles
+
+Since the load for the topics in a bundle might change over time and predicting the load might be hard, bundle split is designed to resolve these challenges. The broker splits a bundle into two and the new smaller bundles can be reassigned to different brokers.
+
+Pulsar supports the following two bundle split algorithms:
+* `range_equally_divide`: split the bundle into two parts with the same hash range size.
+* `topic_count_equally_divide`: split the bundle into two parts with the same number of topics.
+* `specified_positions_divide`: split the bundle into several parts by the specified positions.
+
+:::tip
+
+* The `specified_positions_divide` algorithms only support use by admin API and do not support set into `defaultNamespaceBundleSplitAlgorithm`.
+
+:::
+
+To enable bundle split, you need to configure the following settings in the `broker.conf` file, and set `defaultNamespaceBundleSplitAlgorithm` based on your needs.
+
+```conf
+loadBalancerAutoBundleSplitEnabled=true
+loadBalancerAutoUnloadSplitBundlesEnabled=true
+defaultNamespaceBundleSplitAlgorithm=range_equally_divide
+```
+
+You can configure more parameters for splitting thresholds. Any existing bundle that exceeds any of the thresholds is a candidate to be split. By default, the newly split bundles are immediately reassigned to other brokers, to facilitate the traffic distribution.
+
+```conf
+# maximum topics in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxTopics=1000
+
+# maximum sessions (producers + consumers) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxSessions=1000
+
+# maximum msgRate (in + out) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxMsgRate=30000
+
+# maximum bandwidth (in + out) in a bundle, otherwise bundle split will be triggered
+loadBalancerNamespaceBundleMaxBandwidthMbytes=100
+
+# maximum number of bundles in a namespace (for auto-split)
+loadBalancerNamespaceMaximumBundles=128
+```
+
+## Shed load automatically
+
+The support for automatic load shedding is available in the load manager of Pulsar. This means that whenever the system recognizes a particular broker is overloaded, the system forces some traffic to be reassigned to less-loaded brokers.
+
+When a broker is identified as overloaded, the broker forces to "unload" a subset of the bundles, the ones with higher traffic, that make up for the overload percentage.
+
+For example, the default threshold is 85% and if a broker is over quota at 95% CPU usage, then the broker unloads the percent difference plus a 5% margin: `(95% - 85%) + 5% = 15%`. Given the selection of bundles to unload is based on traffic (as a proxy measure for CPU, network, and memory), the broker unloads bundles for at least 15% of traffic.
+
+:::tip
+
+* The automatic load shedding is enabled by default. To disable it, you can set `loadBalancerSheddingEnabled` to `false`.
+* Besides the automatic load shedding, you can [manually unload bundles](#unload-topics-and-bundles).
+
+:::
+
+Additional settings that apply to shedding:
+
+```conf
+# Load shedding interval. Broker periodically checks whether some traffic should be offload from
+# some over-loaded broker to other under-loaded brokers
+loadBalancerSheddingIntervalMinutes=1
+
+# Prevent the same topics to be shed and moved to other brokers more than once within this timeframe
+loadBalancerSheddingGracePeriodMinutes=30
+```
+
+Pulsar supports the following types of automatic load shedding strategies.
+* [ThresholdShedder](#thresholdshedder)
+* [OverloadShedder](#overloadshedder)
+* [UniformLoadShedder](#uniformloadshedder)
+
+:::note
+
+* From Pulsar 2.10, the **default** shedding strategy is `ThresholdShedder`.
+* You need to restart brokers if the shedding strategy is [dynamically updated](admin-api-brokers.md#dynamic-broker-configuration).
+
+:::
+
+### ThresholdShedder
+
+This strategy sheds bundles from brokers whose resource usage exceeds the average resource usage of brokers in the cluster by a specified threshold.
+
+Current usage for a broker is defined as the maximum usage among CPU, direct memory, throughput in and throughput out values. This value is exponentially smoothed out with historical observations to yield the computed resource usage for broker load balancing purposes.
+
+A broker is considered overloaded if its resource usage exceeds the average resource usage of all brokers in the cluster by at least threshold value `loadBalancerBrokerThresholdShedderPercentage`. Bundles from overloaded brokers are transferred until the expected throughput of the broker is 5% below the cluster average. Furthermore, a broker must have a total current throughput (in + out) sufficiently high in order to be eligible for bundle unloading. If the expected throughput reduction [...]
+
+Note that recently unloaded bundles are not unloaded again.
+
+![Shedding strategy - ThresholdShedder](/assets/shedding-strategy-thresholdshedder.svg)
+
+For example, assume you have three brokers, the average broker usage of broker1 is 40%, the average broker usage of broker2 and broker3 is 10%, then the cluster average usage is 20% ((40% + 10% + 10%) / 3). If you set `loadBalancerBrokerThresholdShedderPercentage` to `10`, then only broker1's certain bundles get unloaded, because the average usage of broker1 is greater than the sum of the cluster average usage (20%) plus `loadBalancerBrokerThresholdShedderPercentage`(10%).
+
+However, in some special cases, the above default strategy cannot leverage the resources of low-load or idle machines.
+
+For example:
+
+There are 11 brokers, of which 10 are loaded at 80% and 1 is loaded at 0%.
+The average load is 80% * 10 / 11 = 72.73%, and the threshold to unload is 72.73% + 10% = 82.73%.
+Since 80% < 82.73%, unload will not be triggered, and there is one idle Broker with load of 0%.
+
+To leverage the resources of low-load or idle machines, you can configure the `lowerBoundarySheddingEnabled` parameter on top of `ThresholdShedder`.
+When `lowerBoundarySheddingEnabled` is set to `true`, a leader decides the lower boundary of the load.
+If `current usage` is less than `average usage - lower boundary load`, e.g., 0% < (82.73% - 10%), the broker with the highest load will be triggered to unload.
+
+To use the `ThresholdShedder` strategy, configure brokers with this value.
+`loadBalancerLoadSheddingStrategy=org.apache.pulsar.broker.loadbalance.impl.ThresholdShedder`
+
+You can configure the weights for each resource per broker in the `conf/broker.conf` file.
+
+```conf
+# The BandWithIn usage weight when calculating new resource usage. The range is between 0 and 1.0.
+loadBalancerBandwithInResourceWeight=1.0
+
+# The BandWithOut usage weight when calculating new resource usage. The range is between 0 and 1.0.
+loadBalancerBandwithOutResourceWeight=1.0
+
+# The CPU usage weight when calculating new resource usage. The range is between 0 and 1.0.
+loadBalancerCPUResourceWeight=1.0
+
+# The heap memory usage weight when calculating new resource usage. The range is between 0 and 1.0.
+loadBalancerMemoryResourceWeight=1.0
+
+# The direct memory usage weight when calculating new resource usage. The range is between 0 and 1.0.
+loadBalancerDirectMemoryResourceWeight=1.0
+```
+
+### OverloadShedder
+This strategy attempts to shed exactly one bundle on brokers which are overloaded, that is, whose maximum system resource usage exceeds [`loadBalancerBrokerOverloadedThresholdPercentage`](#broker-overload-thresholds). To see which resources are considered when determining the maximum system resource. A bundle is recommended for unloading off that broker if and only if the following conditions hold: The broker has at least two bundles assigned and the broker has at least one bundle that h [...]
+
+![Shedding strategy - OverloadShedder](/assets/shedding-strategy-overloadshedder.svg)
+
+To use the `OverloadShedder` strategy, configure brokers with this value.
+`loadBalancerLoadSheddingStrategy=org.apache.pulsar.broker.loadbalance.impl.OverloadShedder`
+
+#### Broker overload thresholds
+
+The determination of when a broker is overloaded is based on the threshold of CPU, network, and memory usage. Whenever either of those metrics reaches the threshold, the system triggers the shedding (if enabled).
+
+:::note
+
+The overload threshold `loadBalancerBrokerOverloadedThresholdPercentage` only applies to the [`OverloadShedder`](#overloadshedder) shedding strategy. By default, it is set to 85%.
+
+:::
+
+Pulsar gathers the CPU, network, and memory usage stats from the system metrics. In some cases of network utilization, the network interface speed that Linux reports is not correct and needs to be manually overridden. This is the case in AWS EC2 instances with 1Gbps NIC speed for which the OS reports 10Gbps speed.
+
+Because of the incorrect max speed, the load manager might think the broker has not reached the NIC capacity, while in fact the broker already uses all the bandwidth and the traffic is slowed down.
+
+You can set `loadBalancerOverrideBrokerNicSpeedGbps` in the `conf/broker.conf` file to correct the max NIC speed. When the value is empty, Pulsar uses the value that the OS reports.
+
+### UniformLoadShedder
+This strategy tends to distribute load uniformly across all brokers. This strategy checks the load difference between the broker with the highest load and the broker with the lowest load. If the difference is higher than configured thresholds `loadBalancerMsgRateDifferenceShedderThreshold` and `loadBalancerMsgThroughputMultiplierDifferenceShedderThreshold` then it finds out bundles that can be unloaded to distribute traffic evenly across all brokers.
+
+![Shedding strategy - UniformLoadShedder](/assets/shedding-strategy-uniformLoadshedder.svg)
+
+To use the `UniformLoadShedder` strategy, configure brokers with this value.
+`loadBalancerLoadSheddingStrategy=org.apache.pulsar.broker.loadbalance.impl.UniformLoadShedder`
+
+## Unload topics and bundles
+
+You can "unload" a topic in Pulsar manual admin operations. Unloading means closing topics, releasing ownership, and reassigning topics to a new broker, based on the current load.
+
+When unloading happens, the client experiences a small latency blip, typically in the order of tens of milliseconds, while the topic is reassigned.
+
+Unloading is the mechanism that the load manager uses to perform the load shedding, but you can also trigger the unloading manually, for example, to correct the assignments and redistribute traffic even before having any broker overloaded.
+
+Unloading a topic has no effect on the assignment, but just closes and reopens the particular topic:
+
+```shell
+pulsar-admin topics unload persistent://tenant/namespace/topic
+```
+
+To unload all topics for a namespace and trigger reassignments:
+
+```shell
+pulsar-admin namespaces unload tenant/namespace
+```
+
+## Distribute anti-affinity namespaces across failure domains
+
+When your application has multiple namespaces and you want one of them available all the time to avoid any downtime, you can group these namespaces and distribute them across different [failure domains](reference-terminology.md#failure-domain) and different brokers. Thus, if one of the failure domains is down (due to release rollout or brokers restart), it only disrupts namespaces owned by that specific failure domain and the rest of the namespaces owned by other domains remain available [...]
+
+Such a group of namespaces has anti-affinity to each other, that is, all the namespaces in this group are [anti-affinity namespaces](reference-terminology.md#anti-affinity-namespaces) and are distributed to different failure domains in a load-balanced manner.
+
+As illustrated in the following figure, Pulsar has 2 failure domains (Domain1 and Domain2) and each domain has 2 brokers in it. You can create an anti-affinity namespace group that has 4 namespaces in it, and all the 4 namespaces have anti-affinity to each other. The load manager tries to distribute namespaces evenly across all the brokers in the same domain. Since each domain has 2 brokers, every broker owns one namespace from this anti-affinity namespace group, and you can see each dom [...]
+
+![Distribute anti-affinity namespaces across failure domains](/assets/anti-affinity-namespaces-across-failure-domains.svg)
+
+The load manager follows an even distribution policy across failure domains to assign anti-affinity namespaces. The following table outlines the even-distributed assignment sequence illustrated in the above figure.
+
+| Assignment sequence | Namespace | Failure domain candidates | Broker candidates | Selected broker |
+|:---|:------------|:------------------|:------------------------------------|:-----------------|
+| 1 | Namespace1 | Domain1, Domain2 | Broker1, Broker2, Broker3, Broker4 | Domain1:Broker1 |
+| 2 | Namespace2 | Domain2          | Broker3, Broker4                   | Domain2:Broker3 |
+| 3 | Namespace3 | Domain1, Domain2 | Broker2, Broker4                   | Domain1:Broker2 |
+| 4 | Namespace4 | Domain2          | Broker4                            | Domain2:Broker4 |
+
+:::tip
+
+* Each namespace belongs to only one anti-affinity group. If a namespace with an existing anti-affinity assignment is assigned to another anti-affinity group, the original assignment is dropped.
+
+* If there are more anti-affinity namespaces than failure domains, the load manager distributes namespaces evenly across all the domains, and also every domain distributes namespaces evenly across all the brokers under that domain.
+
+:::
+
+### Create a failure domain and register brokers
+
+:::note
+
+One broker can only be registered to a single failure domain.
+
+:::
+
+To create a domain under a specific cluster and register brokers, run the following command:
+
+```bash
+pulsar-admin clusters create-failure-domain <cluster-name> --domain-name <domain-name> --broker-list <broker-list-comma-separated>
+```
+
+You can also view, update, and delete domains under a specific cluster. For more information, refer to [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
+
+### Create an anti-affinity namespace group
+
+An anti-affinity group is created automatically when the first namespace is assigned to the group. To assign a namespace to an anti-affinity group, run the following command. It sets an anti-affinity group name for a namespace.
+
+```bash
+pulsar-admin namespaces set-anti-affinity-group <namespace> --group <group-name>
+```
+
+For more information about `anti-affinity-group` related commands, refer to [Pulsar admin docs](pathname:///reference/#/@pulsar:version_reference@/pulsar-admin/).
diff --git a/versioned_docs/version-4.0.x/administration-metadata-store.md b/versioned_docs/version-4.0.x/administration-metadata-store.md
new file mode 100644
index 000000000000..f4ba83adfce8
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-metadata-store.md
@@ -0,0 +1,111 @@
+---
+id: administration-metadata-store
+title: Configure metadata store
+sidebar_label: "Configure metadata store"
+description: Get a comprehensive understanding of metadata store in Pulsar.
+---
+
+Pulsar metadata store maintains all the metadata, configuration, and coordination of a Pulsar cluster, such as topic metadata, schema, broker load data, and so on.
+
+The metadata store of each Pulsar instance should contain the following two components:
+* A local metadata store ensemble (`metadataStoreUrl`) that stores cluster-specific configuration and coordination, such as which brokers are responsible for which topics as well as ownership metadata, broker load reports, and BookKeeper ledger metadata.
+* A configuration store quorum (`configurationMetadataStoreUrl`) stores configuration for clusters, tenants, namespaces, topics, and other entities that need to be globally consistent.
+
+:::note
+
+If you are using a standalone Pulsar or a single Pulsar cluster, you only need to configure one metadata store (via `metadataStoreUrl`) and it also serves as a configuration store.
+
+:::
+
+Pulsar supports the following metadata store services:
+* [Apache ZooKeeper](https://zookeeper.apache.org/)
+* [Etcd](https://etcd.io/)
+* [RocksDB](http://rocksdb.org/)
+* Local memory
+
+:::note
+
+RocksDB and local memory are only applicable to standalone Pulsar or single-node Pulsar clusters.
+
+:::
+
+## Use ZooKeeper as metadata store
+
+Pulsar metadata store can be deployed on a separate ZooKeeper cluster or deployed on an existing ZooKeeper cluster.
+
+To use ZooKeeper as the metadata store, add the following parameters to the `conf/broker.conf` or `conf/standalone.conf` file.
+
+```conf
+metadataStoreUrl=zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181
+configurationMetadataStoreUrl=zk:my-global-zk-1:2181,my-global-zk-2:2181,my-global-zk-3:2181
+```
+
+## Use etcd as metadata store
+
+To use etcd as the metadata store, add the following parameters to the `conf/broker.conf` or `conf/standalone.conf` file.
+
+```conf
+metadataStoreUrl=etcd:http://my-etcd-1:2379,http://my-etcd-2:2379,http://my-etcd-3:2379
+configurationMetadataStoreUrl=etcd:my-global-etcd-1:2379,my-global-etcd-2:2379,my-global-etcd-3:2379
+# metadataStoreConfigPath=/path/to/file
+```
+
+:::tip
+
+The `metadataStoreConfigPath` parameter is required when you want to use the following advanced configurations.
+
+```
+useTls=false
+tlsProvider=JDK
+tlsTrustCertsFilePath=
+tlsKeyFilePath=
+tlsCertificateFilePath=
+authority=
+```
+
+:::
+
+## Use RocksDB as metadata store
+
+To use RocksDB as the metadata store, add the following parameters to the `conf/broker.conf` or `conf/standalone.conf` file.
+
+```conf
+metadataStoreUrl=rocksdb://data/metadata
+# metadataStoreConfigPath=/path/to/file
+```
+
+:::tip
+
+The `metadataStoreConfigPath` parameter is required when you want to use advanced configurations. See [this example](https://github.com/facebook/rocksdb/blob/main/examples/rocksdb_option_file_example.ini) for more information.
+
+:::
+
+## Use local memory as metadata store
+
+To use local memory as the metadata store, add the following parameters to the `conf/broker.conf` or `conf/standalone.conf` file.
+
+```conf
+metadataStoreUrl=memory://local
+```
+
+
+## Enable batch operations on metadata store
+
+Pulsar metadata store supports batch operations and caching to meet low latency and high throughput and improve performance.
+
+To enable batch operations on the metadata store, you can configure the following parameters in the `conf/broker.conf` or `conf/standalone.conf` file.
+
+```conf
+# Whether we should enable metadata operations batching
+metadataStoreBatchingEnabled=true
+
+# Maximum delay to impose on batching grouping
+metadataStoreBatchingMaxDelayMillis=5
+
+# Maximum number of operations to include in a singular batch
+metadataStoreBatchingMaxOperations=1000
+
+# Maximum size of a batch
+metadataStoreBatchingMaxSizeKb=128
+```
+
diff --git a/versioned_docs/version-4.0.x/administration-proxy.md b/versioned_docs/version-4.0.x/administration-proxy.md
new file mode 100644
index 000000000000..923d57ce924c
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-proxy.md
@@ -0,0 +1,124 @@
+---
+id: administration-proxy
+title: Pulsar proxy
+sidebar_label: "Pulsar proxy"
+description: Get a comprehensive understanding of Pulsar proxy.
+---
+
+Pulsar proxy is an optional gateway. Pulsar proxy is used when direct connections between clients and Pulsar brokers are either infeasible or undesirable. For example, when you run Pulsar in a cloud environment or on [Kubernetes](https://kubernetes.io) or an analogous platform, you can run Pulsar proxy.
+
+The Pulsar proxy is not intended to be exposed on the public internet. The security considerations in the current design expect network perimeter security. The requirement of network perimeter security can be achieved with private networks.
+
+If a proxy deployment cannot be protected with network perimeter security, the alternative would be to use [Pulsar's "Proxy SNI routing" feature](concepts-proxy-sni-routing.md) with a properly secured and audited solution. In that case Pulsar proxy component is not used at all.
+
+## Configure the proxy
+
+Before using a proxy, you need to configure it with a broker's address in the cluster. You can configure the broker URL in the proxy configuration, or the proxy to connect directly using service discovery.
+
+> In a production environment service discovery is not recommended.
+
+### Use broker URLs
+
+It is more secure to specify a URL to connect to the brokers.
+
+Proxy authorization requires access to ZooKeeper, so if you use these broker URLs to connect to the brokers, you need to disable authorization at the Proxy level. Brokers still authorize requests after the proxy forwards them.
+
+You can configure the broker URLs in `conf/proxy.conf` as follows.
+
+```properties
+brokerServiceURL=pulsar://brokers.example.com:6650
+brokerWebServiceURL=http://brokers.example.com:8080
+functionWorkerWebServiceURL=http://function-workers.example.com:8080
+```
+
+If you use TLS, configure the broker URLs in the following way:
+
+```properties
+brokerServiceURLTLS=pulsar+ssl://brokers.example.com:6651
+brokerWebServiceURLTLS=https://brokers.example.com:8443
+functionWorkerWebServiceURL=https://function-workers.example.com:8443
+```
+
+The hostname in the URLs provided should be a DNS entry that points to multiple brokers or a virtual IP address, which is backed by multiple broker IP addresses, so that the proxy does not lose connectivity to Pulsar cluster if a single broker becomes unavailable.
+
+The ports to connect to the brokers (6650 and 8080, or in the case of TLS, 6651 and 8443) should be open in the network ACLs.
+
+Note that if you do not use functions, you do not need to configure `functionWorkerWebServiceURL`.
+
+### Use service discovery
+
+Pulsar uses [ZooKeeper](https://zookeeper.apache.org) for service discovery. To connect the proxy to ZooKeeper, specify the following in `conf/proxy.conf`.
+
+```properties
+metadataStoreUrl=my-zk-0:2181,my-zk-1:2181,my-zk-2:2181
+configurationMetadataStoreUrl=my-zk-0:2184,my-zk-remote:2184
+```
+
+> To use service discovery, you need to open the network ACLs, so the proxy can connects to the ZooKeeper nodes through the ZooKeeper client port (port `2181`) and the configuration store client port (port `2184`).
+
+> However, it is not secure to use service discovery. Because if the network ACL is open, when someone compromises a proxy, they have full access to ZooKeeper.
+
+### Restricting target broker addresses to mitigate CVE-2022-24280
+
+The Pulsar Proxy trusts clients to provide valid target broker addresses to connect to.
+Unless the Pulsar Proxy is explicitly configured to limit access, the Pulsar Proxy is vulnerable as described in the security advisory [Apache Pulsar Proxy target broker address isn't validated (CVE-2022-24280)](https://github.com/apache/pulsar/wiki/CVE-2022-24280).
+
+It is necessary to limit proxied broker connections to known broker addresses by specifying `brokerProxyAllowedHostNames` and `brokerProxyAllowedIPAddresses` settings.
+
+When specifying `brokerProxyAllowedHostNames`, it's possible to use a wildcard.
+Please notice that `*` is a wildcard that matches any character in the hostname. It also matches dot `.` characters.
+
+It is recommended to use a pattern that matches only the desired brokers and no other hosts in the local network. Pulsar lookups will use the default host name of the broker by default. This can be overridden with the `advertisedAddress` setting in `broker.conf`.
+
+To increase security, it is also possible to restrict access with the `brokerProxyAllowedIPAddresses` setting. It is not mandatory to configure `brokerProxyAllowedIPAddresses` when `brokerProxyAllowedHostNames` is properly configured so that the pattern matches only the target brokers.
+`brokerProxyAllowedIPAddresses` setting supports a comma separate list of IP address, IP address ranges and IP address networks [(supported format reference)](https://seancfoley.github.io/IPAddress/IPAddress/apidocs/inet/ipaddr/IPAddressString.html).
+
+Example: limiting by host name in a Kubernetes deployment
+```yaml
+  # example of limiting to Kubernetes statefulset hostnames that contain "broker-"
+  PULSAR_PREFIX_brokerProxyAllowedHostNames: '*broker-*.*.*.svc.cluster.local'
+```
+
+Example: limiting by both host name and ip address in a `proxy.conf` file for host deployment.
+```properties
+# require "broker" in host name
+brokerProxyAllowedHostNames=*broker*.localdomain
+# limit target ip addresses to a specific network
+brokerProxyAllowedIPAddresses=10.0.0.0/8
+```
+
+Example: limiting by multiple host name patterns and multiple ip address ranges in a `proxy.conf` file for host deployment.
+```properties
+# require "broker" in host name
+brokerProxyAllowedHostNames=*broker*.localdomain,*broker*.otherdomain
+# limit target ip addresses to a specific network or range demonstrating multiple supported formats
+brokerProxyAllowedIPAddresses=10.10.0.0/16,192.168.1.100-120,172.16.2.*,10.1.2.3
+```
+
+
+## Start the proxy
+
+To start the proxy, run the following commands.
+
+```bash
+cd /path/to/pulsar/directory
+bin/pulsar proxy \
+    --metadata-store zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181 \
+    --configuration-metadata-store zk:my-zk-1:2181,my-zk-2:2181,my-zk-3:2181
+```
+
+> You can run multiple instances of the Pulsar proxy in a cluster.
+
+## Stop the proxy
+
+Pulsar proxy runs in the foreground by default. To stop the proxy, simply stop the process in which the proxy is running.
+
+## Proxy frontends
+
+You can run Pulsar proxy behind some kind of load-distributing frontend, such as an [HAProxy](https://www.digitalocean.com/community/tutorials/an-introduction-to-haproxy-and-load-balancing-concepts) load balancer.
+
+## Use Pulsar clients with the proxy
+
+Once your Pulsar proxy is up and running, preferably behind a load-distributing [frontend](#proxy-frontends), clients can connect to the proxy via whichever address that the frontend uses. If the address is the DNS address `pulsar.cluster.default`, for example, the connection URL for clients is `pulsar://pulsar.cluster.default:6650`.
+
+For more information on Proxy configuration, refer to [Pulsar proxy](reference-configuration.md#pulsar-proxy).
diff --git a/versioned_docs/version-4.0.x/administration-pulsar-manager.md b/versioned_docs/version-4.0.x/administration-pulsar-manager.md
new file mode 100644
index 000000000000..5661020da4ee
--- /dev/null
+++ b/versioned_docs/version-4.0.x/administration-pulsar-manager.md
@@ -0,0 +1,191 @@
+---
+id: administration-pulsar-manager
+title: Pulsar Manager
+sidebar_label: "Pulsar Manager"
+description: Get a comprehensive understanding of concepts, installation, and configuration of Pulsar Manager.
+---
+
+Pulsar Manager is a web-based GUI management and monitoring tool that helps administrators and users manage and monitor tenants, namespaces, topics, subscriptions, brokers, clusters, and so on, and supports dynamic configuration of multiple environments.
+
+## Install
+
+To install Pulsar Manager, complete the following steps.
+
+### 1. Quick Install
+
+The easiest way to use the Pulsar Manager is to run it inside a Docker container.
+
+```shell
+docker pull apachepulsar/pulsar-manager:latest
+docker run -it \
+  -p 9527:9527 -p 7750:7750 \
+  -e SPRING_CONFIGURATION_FILE=/pulsar-manager/pulsar-manager/application.properties \
+  apachepulsar/pulsar-manager:latest
+```
+
+* Pulsar Manager is divided into front-end and back-end, the front-end service port is `9527` and the back-end service port is `7750`.
+* `SPRING_CONFIGURATION_FILE`: Default configuration file for spring.
+* By default, Pulsar Manager uses the `herddb` database. HerdDB is a SQL distributed database implemented in Java and can be found at [herddb.org](https://herddb.org/) for more information.
+
+### 2. Configure Database or JWT authentication
+
+#### Configure Database (optional)
+
+If you have a large amount of data, you can use a custom database. Otherwise, some display errors may occur. For example, the topic information cannot be displayed when the topic exceeds 10000.
+The following is an example of PostgreSQL.
+
+1. Initialize database and table structures using the [file](https://github.com/apache/pulsar-manager/blob/master/src/main/resources/META-INF/sql/postgresql-schema.sql).
+2. Download and modify the [configuration file](https://github.com/apache/pulsar-manager/blob/master/src/main/resources/application.properties), then add the PostgreSQL configuration.
+
+```properties
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/pulsar_manager
+spring.datasource.username=postgres
+spring.datasource.password=postgres
+```
+
+3. Add a configuration mount and start with a docker image.
+
+```bash
+docker pull apachepulsar/pulsar-manager:v0.3.0
+docker run -it \
+    -p 9527:9527 -p 7750:7750 \
+    -v /your-path/application.properties:/pulsar-manager/pulsar-manager/application.properties
+    -e SPRING_CONFIGURATION_FILE=/pulsar-manager/pulsar-manager/application.properties \
+    apachepulsar/pulsar-manager:v0.3.0
+```
+
+#### Enable JWT authentication (optional)
+
+If you want to turn on JWT authentication, configure the `application.properties` file.
+
+```properties
+backend.jwt.token=token
+
+jwt.broker.token.mode=PRIVATE
+jwt.broker.public.key=file:///path/broker-public.key
+jwt.broker.private.key=file:///path/broker-private.key
+
+or
+jwt.broker.token.mode=SECRET
+jwt.broker.secret.key=file:///path/broker-secret.key
+```
+
+* `backend.jwt.token`: token for the superuser. You need to configure this parameter during cluster initialization.
+* `jwt.broker.token.mode`: multiple modes of generating tokens, including PUBLIC, PRIVATE, and SECRET.
+* `jwt.broker.public.key`: configure this option if you use the PUBLIC mode.
+* `jwt.broker.private.key`: configure this option if you use the PRIVATE mode.
+* `jwt.broker.secret.key`: configure this option if you use the SECRET mode.
+For more information, see [Token Authentication Admin of Pulsar](security-token-admin.md).
+
+Docker command to add profile and key files mount.
+
+```bash
+docker pull apachepulsar/pulsar-manager:v0.3.0
+docker run -it \
+  -p 9527:9527 -p 7750:7750 \
+  -v /your-path/application.properties:/pulsar-manager/pulsar-manager/application.properties
+  -v /your-path/private.key:/pulsar-manager/private.key
+  -e SPRING_CONFIGURATION_FILE=/pulsar-manager/pulsar-manager/application.properties \
+  apachepulsar/pulsar-manager:v0.3.0
+```
+
+### 3. Set the administrator account and password
+
+```bash
+CSRF_TOKEN=$(curl http://localhost:7750/pulsar-manager/csrf-token)
+curl \
+   -H 'X-XSRF-TOKEN: $CSRF_TOKEN' \
+   -H 'Cookie: XSRF-TOKEN=$CSRF_TOKEN;' \
+   -H "Content-Type: application/json" \
+   -X PUT http://localhost:7750/pulsar-manager/users/superuser \
+   -d '{"name": "admin", "password": "apachepulsar", "description": "test", "email": "username@test.org"}'
+```
+
+The request parameter in curl command:
+
+```json
... 45873 lines suppressed ...