diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index 0289ea601eab0e3737f72de15cd73256484a67ed..b6c9dd138b6f63a06d1063fad10eca772ff91ca1 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -5871,6 +5871,8 @@ dependencies = [
 name = "pallet-bags-list"
 version = "4.0.0-dev"
 dependencies = [
+ "aquamarine",
+ "docify",
  "frame-benchmarking",
  "frame-election-provider-support",
  "frame-support",
diff --git a/substrate/frame/bags-list/Cargo.toml b/substrate/frame/bags-list/Cargo.toml
index a2974a5e171f07a579a8ff3ba12822283107ffdc..222d64dc229f657b7486f7355def25f61307e7a3 100644
--- a/substrate/frame/bags-list/Cargo.toml
+++ b/substrate/frame/bags-list/Cargo.toml
@@ -27,6 +27,8 @@ frame-election-provider-support = { version = "4.0.0-dev", default-features = fa
 
 # third party
 log = { version = "0.4.17", default-features = false }
+docify = "0.2.1"
+aquamarine = { version = "0.3.2" }
 
 # Optional imports for benchmarking
 frame-benchmarking = { version = "4.0.0-dev", path = "../benchmarking", optional = true, default-features = false }
diff --git a/substrate/frame/bags-list/src/lib.rs b/substrate/frame/bags-list/src/lib.rs
index 156c52cc87c4558ca5c1699b40ddfa0d84aa5090..a5d3257b734bb644b06967540a2435eec0236aa2 100644
--- a/substrate/frame/bags-list/src/lib.rs
+++ b/substrate/frame/bags-list/src/lib.rs
@@ -15,21 +15,52 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! # Bags-List Pallet
+//! > Made with *Substrate*, for *Polkadot*.
 //!
-//! A semi-sorted list, where items hold an `AccountId` based on some `Score`. The
-//! `AccountId` (`id` for short) might be synonym to a `voter` or `nominator` in some context, and
-//! `Score` signifies the chance of each id being included in the final
-//! [`SortedListProvider::iter`].
+//! [![github]](https://github.com/paritytech/substrate/frame/fast-unstake) -
+//! [![polkadot]](https://polkadot.network)
 //!
-//! It implements [`frame_election_provider_support::SortedListProvider`] to provide a semi-sorted
-//! list of accounts to another pallet. It needs some other pallet to give it some information about
-//! the weights of accounts via [`frame_election_provider_support::ScoreProvider`].
+//! [polkadot]:
+//!     https://img.shields.io/badge/polkadot-E6007A?style=for-the-badge&logo=polkadot&logoColor=white
+//! [github]:
+//!     https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
 //!
-//! This pallet is not configurable at genesis. Whoever uses it should call appropriate functions of
-//! the `SortedListProvider` (e.g. `on_insert`, or `unsafe_regenerate`) at their genesis.
+//!  # Bags-List Pallet
 //!
-//! # Goals
+//! An onchain implementation of a semi-sorted linked list, with permissionless sorting and update
+//! operations.
+//!
+//! ## Pallet API
+//!
+//! See the [`pallet`] module for more information about the interfaces this pallet exposes,
+//! including its configuration trait, dispatchables, storage items, events and errors.
+//!
+//! This pallet provides an implementation of
+//! [`frame_election_provider_support::SortedListProvider`] and it can typically be used by another
+//! pallet via this API.
+//!
+//! ## Overview
+//!
+//! This pallet splits `AccountId`s into different bags. Within a bag, these `AccountId`s are stored
+//! as nodes in a linked-list manner. This pallet then provides iteration over all bags, which
+//! basically allows an infinitely large list of items to be kept in a sorted manner.
+//!
+//! Each bags has a upper and lower range of scores, denoted by [`Config::BagThresholds`]. All nodes
+//! within a bag must be within the range of the bag. If not, the permissionless [`Pallet::rebag`]
+//! can be used to move any node to the right bag.
+//!
+//! Once a `rebag` happens, the order within a node is still not enforced. To move a node to the
+//! optimal position in a bag, the [`Pallet::put_in_front_of`] or [`Pallet::put_in_front_of_other`]
+//! can be used.
+//!
+//! Additional reading, about how this pallet is used in the context of Polkadot's staking system:
+//! <https://polkadot.network/blog/staking-update-september-2021/#bags-list-in-depth>
+//!
+//! ## Examples
+//!
+//! See [`example`] for a diagram of `rebag` and `put_in_front_of` operations.
+//!
+//! ## Low Level / Implementation Details
 //!
 //! The data structure exposed by this pallet aims to be optimized for:
 //!
@@ -37,7 +68,7 @@
 //! - iteration over the top* N items by score, where the precise ordering of items doesn't
 //!   particularly matter.
 //!
-//! # Details
+//! ### Further Details
 //!
 //! - items are kept in bags, which are delineated by their range of score (See
 //!   [`Config::BagThresholds`]).
@@ -53,6 +84,44 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
+#[cfg(doc)]
+#[cfg_attr(doc, aquamarine::aquamarine)]
+///
+/// In this example, assuming each node has an equal id and score (eg. node 21 has a score of 21),
+/// the node 22 can be moved from bag 1 to bag 0 with the `rebag` operation.
+///
+/// Once the whole list is iterated, assuming the above above rebag happens, the order of iteration
+/// would be: `25, 21, 22, 12, 22, 5, 7, 3`.
+///
+/// Moreover, in bag2, node 7 can be moved to the front of node 5 with the `put_in_front_of`, as it
+/// has a higher score.
+///
+/// ```mermaid
+/// graph LR
+/// 	Bag0 --> Bag1 --> Bag2
+///
+/// 	subgraph Bag0[Bag 0: 21-30 DOT]
+/// 		direction LR
+/// 		25 --> 21 --> 22X[22]
+/// 	end
+///
+/// 	subgraph Bag1[Bag 1: 11-20 DOT]
+/// 		direction LR
+/// 		12 --> 22
+/// 	end
+///
+/// 	subgraph Bag2[Bag 2: 0-10 DOT]
+/// 		direction LR
+/// 		5 --> 7 --> 3
+/// 	end
+///
+/// 	style 22X stroke-dasharray: 5 5,opacity:50%
+/// ```
+///
+/// The equivalent of this in code would be:
+#[doc = docify::embed!("src/tests.rs", examples_work)]
+pub mod example {}
+
 use codec::FullCodec;
 use frame_election_provider_support::{ScoreProvider, SortedListProvider};
 use frame_system::ensure_signed;
@@ -240,9 +309,11 @@ pub mod pallet {
 		/// Move the caller's Id directly in front of `lighter`.
 		///
 		/// The dispatch origin for this call must be _Signed_ and can only be called by the Id of
-		/// the account going in front of `lighter`.
+		/// the account going in front of `lighter`. Fee is payed by the origin under all
+		/// circumstances.
+		///
+		/// Only works if:
 		///
-		/// Only works if
 		/// - both nodes are within the same bag,
 		/// - and `origin` has a greater `Score` than `lighter`.
 		#[pallet::call_index(1)]
@@ -257,6 +328,24 @@ pub mod pallet {
 				.map_err::<Error<T, I>, _>(Into::into)
 				.map_err::<DispatchError, _>(Into::into)
 		}
+
+		/// Same as [`Pallet::put_in_front_of`], but it can be called by anyone.
+		///
+		/// Fee is paid by the origin under all circumstances.
+		#[pallet::call_index(2)]
+		#[pallet::weight(T::WeightInfo::put_in_front_of())]
+		pub fn put_in_front_of_other(
+			origin: OriginFor<T>,
+			heavier: AccountIdLookupOf<T>,
+			lighter: AccountIdLookupOf<T>,
+		) -> DispatchResult {
+			let _ = ensure_signed(origin)?;
+			let lighter = T::Lookup::lookup(lighter)?;
+			let heavier = T::Lookup::lookup(heavier)?;
+			List::<T, I>::put_in_front_of(&lighter, &heavier)
+				.map_err::<Error<T, I>, _>(Into::into)
+				.map_err::<DispatchError, _>(Into::into)
+		}
 	}
 
 	#[pallet::hooks]
diff --git a/substrate/frame/bags-list/src/tests.rs b/substrate/frame/bags-list/src/tests.rs
index 74f1491835a3229e9f631cb8b974d43b1148c0d7..9e8508698d8e8f613b1b78fb6472386d046d9c98 100644
--- a/substrate/frame/bags-list/src/tests.rs
+++ b/substrate/frame/bags-list/src/tests.rs
@@ -22,6 +22,62 @@ use frame_election_provider_support::{SortedListProvider, VoteWeight};
 use list::Bag;
 use mock::{test_utils::*, *};
 
+#[docify::export]
+#[test]
+fn examples_work() {
+	ExtBuilder::default()
+		.skip_genesis_ids()
+		// initially set the score of 11 for 22 to push it next to 12
+		.add_ids(vec![(25, 25), (21, 21), (12, 12), (22, 11), (5, 5), (7, 7), (3, 3)])
+		.build_and_execute(|| {
+			// initial bags
+			assert_eq!(
+				List::<Runtime>::get_bags(),
+				vec![
+					// bag 0 -> 10
+					(10, vec![5, 7, 3]),
+					// bag 10 -> 20
+					(20, vec![12, 22]),
+					// bag 20 -> 30
+					(30, vec![25, 21])
+				]
+			);
+
+			// set score of 22 to 22
+			StakingMock::set_score_of(&22, 22);
+
+			// now we rebag 22 to the first bag
+			assert_ok!(BagsList::rebag(RuntimeOrigin::signed(42), 22));
+
+			assert_eq!(
+				List::<Runtime>::get_bags(),
+				vec![
+					// bag 0 -> 10
+					(10, vec![5, 7, 3]),
+					// bag 10 -> 20
+					(20, vec![12]),
+					// bag 20 -> 30
+					(30, vec![25, 21, 22])
+				]
+			);
+
+			// now we put 7 at the front of bag 0
+			assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(7), 5));
+
+			assert_eq!(
+				List::<Runtime>::get_bags(),
+				vec![
+					// bag 0 -> 10
+					(10, vec![7, 5, 3]),
+					// bag 10 -> 20
+					(20, vec![12]),
+					// bag 20 -> 30
+					(30, vec![25, 21, 22])
+				]
+			);
+		})
+}
+
 mod pallet {
 	use super::*;
 
@@ -207,6 +263,25 @@ mod pallet {
 		})
 	}
 
+	#[test]
+	fn put_in_front_of_other_can_be_permissionless() {
+		ExtBuilder::default()
+			.skip_genesis_ids()
+			.add_ids(vec![(10, 15), (11, 16), (12, 19)])
+			.build_and_execute(|| {
+				// given
+				assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![10, 11, 12])]);
+				// 11 now has more weight than 10 and can be moved before it.
+				StakingMock::set_score_of(&11u32, 17);
+
+				// when
+				assert_ok!(BagsList::put_in_front_of_other(RuntimeOrigin::signed(42), 11u32, 10));
+
+				// then
+				assert_eq!(List::<Runtime>::get_bags(), vec![(20, vec![11, 10, 12])]);
+			});
+	}
+
 	#[test]
 	fn put_in_front_of_two_node_bag_heavier_is_tail() {
 		ExtBuilder::default()
@@ -368,7 +443,7 @@ mod pallet {
 			StakingMock::set_score_of(&4, 999);
 
 			// when
-			BagsList::put_in_front_of(RuntimeOrigin::signed(2), 4).unwrap();
+			assert_ok!(BagsList::put_in_front_of(RuntimeOrigin::signed(2), 4));
 
 			// then
 			assert_eq!(List::<Runtime>::get_bags(), vec![(10, vec![1]), (1_000, vec![3, 2, 4])]);