Here is an end to end example of creating and executing a multisig with Polkadot JS:

```javascript
const { ApiPromise, WsProvider } = require('@polkadot/api');
const { Keyring } = require('@polkadot/keyring');
const {
	cryptoWaitReady,
	createKeyMulti,
	encodeAddress,
	sortAddresses,
} = require('@polkadot/util-crypto');

const SS58Prefix = 2;

async function multisigTransfer() {
	await cryptoWaitReady();

	// create a keyring with some non-default values specified
	const keyring = new Keyring({ type: 'sr25519', ss58Format: SS58Prefix });

	// Connect to the local Polkadot node
	const wsProvider = new WsProvider('ws://localhost:9944');
	const api = await ApiPromise.create({ provider: wsProvider });

	// Retrieve the chain & node information via rpc calls
	const [chain, nodeName, nodeVersion] = await Promise.all([
		api.rpc.system.chain(),
		api.rpc.system.name(),
		api.rpc.system.version()
	]);

	console.log(`You are connected to chain ${chain} using ${nodeName} v${nodeVersion}`);

	// create accounts based on the development seed
	const alice = keyring.addFromUri('//Alice');
	const bob = keyring.addFromUri('//Bob');
	const charlie = keyring.addFromUri('//Charlie');

	// Define the multisig transfer details
	const multisigCall = api.tx.balances.transfer(alice.address, 1_234_567);
	const addresses = [alice.address, bob.address, charlie.address]
	const threshold = 2;
	const { weight: maxWeight } = await multisigCall.paymentInfo(alice);

	// Address as a byte array.
	const multisigAddress = encodeAddress(createKeyMulti(addresses, threshold), SS58Prefix);
	console.log(multisigAddress);

	await fundAddress(api, alice, multisigAddress, 1_000_000_000_000)

	await approveMultisigWithHash(api, alice, threshold, addresses, null, multisigCall.method.hash, maxWeight);

	const info = await api.query.multisig.multisigs(multisigAddress, multisigCall.method.hash);
	const timepoint = info.unwrap().when;

	await approveMultisigWithHash(api, bob, threshold, addresses, timepoint, multisigCall.method.hash, maxWeight);
	await finalizeMultisig(api, charlie, threshold, addresses, timepoint, multisigCall, maxWeight);
}

async function fundAddress(api, sender, to, amount) {
	return new Promise((resolve, reject) => {
		api.tx.balances
			.transfer(to, amount)
			.signAndSend(sender, async (result) => {
				console.log(`Current status is ${result.status}`);

				if (result.status.isInBlock) {
					console.log(`Transfer completed at blockHash ${result.status.asInBlock}`);
					resolve();
				}
			}).catch((error) => {
				reject(error);
			});
	});
}

async function approveMultisigWithHash(api, sender, threshold, addresses, timepoint, callHash, maxWeight) {
	return new Promise((resolve, reject) => {
		// Take addresses and remove the sender.
		const otherSignatories = addresses.filter((who) => who !== sender.address);

		// Sort them by public key.
		const otherSignatoriesSorted = sortAddresses(otherSignatories, SS58Prefix);

		api.tx.multisig
			.approveAsMulti(threshold, otherSignatoriesSorted, timepoint, callHash, maxWeight)
			.signAndSend(sender, async (result) => {
				console.log(`Current status is ${result.status}`);

				if (result.status.isInBlock) {
					console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
					resolve();
				}
			}).catch((error) => {
				reject(error);
			});
	});
}

async function finalizeMultisig(api, sender, threshold, addresses, timepoint, multisigCall, maxWeight) {
	return new Promise((resolve, reject) => {
		// Take addresses and remove the sender.
		const otherSignatories = addresses.filter((who) => who !== sender.address);

		// Sort them by public key.
		const otherSignatoriesSorted = sortAddresses(otherSignatories, SS58Prefix);

		api.tx.multisig
			.asMulti(threshold, otherSignatoriesSorted, timepoint, multisigCall, maxWeight)
			.signAndSend(sender, async (result) => {
				console.log(`Current status is ${result.status}`);

				if (result.status.isInBlock) {
					console.log(`Transaction included at blockHash ${result.status.asInBlock}`);
					resolve();
				}
			}).catch((error) => {
				reject(error);
			});
	});
}

multisigTransfer().catch(console.error);
```

Here you can see all the events emitted, showing the end to end scenario works on a local polkadot dev envrionment:

[![enter image description here][1]][1]

Here was a resource I found which helped me complete the scenario:

https://github.com/polkadot-js/api/issues/2878


  [1]: https://i.sstatic.net/4TsKHaLj.png