diff --git a/src/pages/_meta.json b/src/pages/_meta.json
index f04ea4b2..7095d526 100644
--- a/src/pages/_meta.json
+++ b/src/pages/_meta.json
@@ -1,6 +1,7 @@
{
"index": "Welcome",
"core": "CosmWasm Core",
+ "ibc": "IBC",
"sylvia": "Sylvia",
"cw-multi-test": "MultiTest",
"how-to-doc": "How to doc",
diff --git a/src/pages/ibc.mdx b/src/pages/ibc.mdx
new file mode 100644
index 00000000..3c911e85
--- /dev/null
+++ b/src/pages/ibc.mdx
@@ -0,0 +1,18 @@
+---
+tags: ["ibc"]
+---
+
+# Introduction
+
+IBC is a protocol that allows different blockchains to communicate with each
+other. It is a standard that defines how blockchains can send and receive
+messages to each other. This allows for the creation of a network of blockchains
+that can interact with each other.
+
+You can use the IBC protocol as a building block to create your own custom
+protocols on top of it, but you can also use existing protocols like the [ICS-20
+token transfer protocol]. In the following sections, we will explain how both of
+these work.
+
+[ICS-20 token transfer protocol]:
+ https://github.com/cosmos/ibc/blob/main/spec/app/ics-020-fungible-token-transfer/README.md
diff --git a/src/pages/ibc/_meta.json b/src/pages/ibc/_meta.json
new file mode 100644
index 00000000..7135e07f
--- /dev/null
+++ b/src/pages/ibc/_meta.json
@@ -0,0 +1,6 @@
+{
+ "getting-started": "Getting started",
+ "basic-concepts": "Basic concepts",
+ "existing-protocols": "Using existing protocols",
+ "diy-protocol": "Build your own protocol"
+}
diff --git a/src/pages/ibc/basic-concepts.mdx b/src/pages/ibc/basic-concepts.mdx
new file mode 100644
index 00000000..2a41857d
--- /dev/null
+++ b/src/pages/ibc/basic-concepts.mdx
@@ -0,0 +1,26 @@
+---
+tags: ["ibc"]
+---
+
+## Basic Concepts
+
+In order to understand how IBC works, it is important to understand some basic
+concepts:
+
+- **Port**: An identifier that corresponds to a single module on a chain. One
+ module can have multiple ports. Each contract has its own unique port.
+- **Channel**: A connection between two ports on different blockchains that
+ allows them to send packets to each other. Each port can have multiple
+ channels.
+- **Relayer**: A service that is responsible for relaying packets between
+ blockchains. Anyone can run a relayer.
+- **Packet**: A piece of binary data that is sent through a channel. It can time
+ out if it is not delivered within a certain time frame.
+
+We will go into more detail on how these concepts are related to CosmWasm in the
+later sections, but this should give you some basic terminology to start with.
+If you want to learn more about IBC, you can read the [IBC specification] or
+check out the [IBC documentation].
+
+[IBC specification]: https://github.com/cosmos/ibc
+[IBC documentation]: https://ibc.cosmos.network/main
diff --git a/src/pages/ibc/diy-protocol.mdx b/src/pages/ibc/diy-protocol.mdx
new file mode 100644
index 00000000..7252f9e9
--- /dev/null
+++ b/src/pages/ibc/diy-protocol.mdx
@@ -0,0 +1,10 @@
+---
+tags: ["ibc"]
+---
+
+# Build your own protocol
+
+In the following sections, we will guide you through the process of building
+your own IBC protocol. We will cover how to establish a channel between two
+chains, send and receive packets and how to handle timeouts and
+acknowledgements.
diff --git a/src/pages/ibc/diy-protocol/_meta.json b/src/pages/ibc/diy-protocol/_meta.json
new file mode 100644
index 00000000..80959cc4
--- /dev/null
+++ b/src/pages/ibc/diy-protocol/_meta.json
@@ -0,0 +1,4 @@
+{
+ "channel-lifecycle": "Channel lifecycle",
+ "packet-lifecycle": "Packet lifecycle"
+}
diff --git a/src/pages/ibc/diy-protocol/channel-lifecycle.mdx b/src/pages/ibc/diy-protocol/channel-lifecycle.mdx
new file mode 100644
index 00000000..5a36b2e9
--- /dev/null
+++ b/src/pages/ibc/diy-protocol/channel-lifecycle.mdx
@@ -0,0 +1,35 @@
+---
+tags: ["ibc"]
+---
+
+# Channel lifecycle
+
+A channel is a connection between two IBC ports that allows them to send packets
+to each other. In this section, we will cover how to establish a channel and how
+to close it. Since a channel is a connection between two ports, it can connect
+two chain modules, two contracts, or a module and a contract.
+
+## Establishing a channel
+
+In order to send packets between two chains, you need to establish a channel
+between them. This process involves two calls per chain. They are described
+below. Once the channel is established, you can start sending packets through
+it, which we will cover in the next section.
+
+### Channel open
+
+{/* `ibc_channel_open` called on both chains, explain differences */}
+
+### Channel connect
+
+{/* `ibc_channel_connect` called on both chains, explain differences */}
+
+## Closing a channel
+
+---
+
+Notes:
+
+- reference basic concepts
+- relayer initiates this
+- explain ibc_channel_open & ibc_channel_connect
diff --git a/src/pages/ibc/diy-protocol/packet-lifecycle.mdx b/src/pages/ibc/diy-protocol/packet-lifecycle.mdx
new file mode 100644
index 00000000..b728278e
--- /dev/null
+++ b/src/pages/ibc/diy-protocol/packet-lifecycle.mdx
@@ -0,0 +1,17 @@
+---
+tags: ["ibc"]
+---
+
+# Packet lifecycle
+
+## Sending a packet
+
+## Receiving a packet
+
+### Async acknowledgement
+
+if implemented until then
+
+## Receiving a packet acknowledgement
+
+## Receiving a packet timeout
diff --git a/src/pages/ibc/existing-protocols.mdx b/src/pages/ibc/existing-protocols.mdx
new file mode 100644
index 00000000..fb18893e
--- /dev/null
+++ b/src/pages/ibc/existing-protocols.mdx
@@ -0,0 +1,138 @@
+---
+tags: ["ibc", "ics20"]
+---
+
+import { Callout } from "nextra/components";
+
+# Using existing protocols
+
+The easiest way to use IBC is to use an already existing protocol. These
+protocols can either be implemented by the chain itself or by another contract.
+
+One example for the former is the `IbcMsg::Transfer` message, which causes an
+ICS20 transfer. This message is included in the CosmWasm standard library and
+causes the chain's IBC transfer module to send tokens to another chain.
+
+An example for the latter is the Nois protocol. It provides a proxy contract
+that handles all the IBC logic for you. For more information on Nois, check the
+[Nois documentation](https://docs.nois.network/dapp_devs/use_nois_randomness.html).
+We will later cover how to implement your own IBC protocol.
+
+## Example: `IbcMsg::Transfer`
+
+To initiate an ICS20 transfer, you need to attach an `IbcMsg::Transfer` message
+to your contract response like this:
+
+```rust template="execute"
+// construct the transfer message
+let msg = IbcMsg::Transfer {
+ channel_id: "channel-0".to_string(),
+ to_address: "cosmos1exampleaddress".to_string(),
+ amount: Coin::new(123u128, "ucoin"),
+ timeout: env.block.time.plus_seconds(60).into(),
+ memo: None,
+};
+
+// attach the message and return the response
+Ok(Response::new().add_message(msg))
+```
+
+The `channel_id` is the identifier of the channel you want to use for the
+transfer. Which channel that should be depends on the source and destination
+chain. You can find out the correct channel ID using a
+[block explorer](https://www.mintscan.io/cosmos/relayers).
+
+The `to_address` is the address on the _destination chain_ that should receive
+the tokens.
+
+The `amount` is the number and denomination of tokens to send. On the
+destination chain, the same amount will be received, but the denomination will
+be of the form `ibc/HASH`, where `HASH` is a SHA256 hash uniquely identifying
+the channel and the source chain denomination. To learn more about this, take a
+look at the [Cosmos Developer Portal].
+
+The `timeout` can either be a timestamp or a block height, as measured on the
+destination chain. It is used to prevent the transfer from being stuck in limbo
+if the destination chain does not receive the packet.
+
+The `memo` is an optional field that can be used to attach a message to the
+transfer. It is often used for additional functionality like
+[packet-forward-middleware] or IBC Callbacks.
+
+[packet-forward-middleware]:
+ https://github.com/cosmos/ibc-apps/tree/main/middleware/packet-forward-middleware
+[Cosmos Developer Portal]:
+ https://tutorials.cosmos.network/tutorials/6-ibc-dev/#understand-ibc-denoms
+
+## ADR-8: IBC Callbacks
+
+When you send an ICS20 transfer as described above, you do not get any feedback
+on whether the transfer was successful or not and the destination does not get
+informed of its newfound wealth. To solve this problem, the ADR-8 specification
+was created. On the source chain, it provides callbacks when an IBC packet was
+acknowledged or timed out. On the destination chain, it triggers callbacks when
+a packet is received.
+
+
+ To receive callbacks, the chain needs to support IBC Callbacks for the message
+ type.
+
+
+### Enabling IBC Callbacks for a message
+
+You need to explicitly opt-in to IBC Callbacks for each message. In order to do
+this, you need to add some metadata to the message, including who should receive
+the callbacks.
+
+
+ The exact data format and how to add it to the message can vary, but for the
+ `IbcMsg::Transfer` message, this data is in JSON format and needs to be added
+ to the `memo` field.
+
+
+To make this as easy as possible, we provide a helper type `IbcCallbackRequest`
+that you can use to generate the JSON:
+
+{/* TODO: add `template="execute"` once IBC Callbacks are merged */}
+
+```rust
+let _ = IbcMsg::Transfer {
+ to_address: "cosmos1exampleaddress".to_string(),
+ channel_id: "channel-0".to_string(),
+ amount: Coin::new(10u32, "ucoin"),
+ timeout: Timestamp::from_seconds(12345).into(),
+ memo: Some(to_json_string(&IbcCallbackRequest::both(IbcSrcCallback {
+ address: env.contract.address,
+ gas_limit: None,
+ }, IbcDstCallback {
+ address: to_address.clone(),
+ gas_limit: None,
+ })).unwrap()),
+};
+```
+
+As you can see, you can request callbacks for both the source and destination
+chain. However, you can also request callbacks for only one of them. For this,
+you need to provide the address that should receive the callback and you can
+optionally set a gas limit for the callback execution. Please take a look at the
+`IbcCallbackRequest` docs for more information.
+
+
+ The `address` of the source callback always needs to be the contract address
+ that sends the message (`env.contract.address`). Otherwise, the callback will
+ error and the contract will not be called.
+
+
+### Entrypoints
+
+TODO
+
+- two new entrypoints
+ - `ibc_source_chain_callback`
+ - `ibc_destination_chain_callback`
+
+---
+
+Notes:
+
+- add link to IbcCallbackRequest docs when merged and deployed
diff --git a/src/pages/ibc/getting-started.mdx b/src/pages/ibc/getting-started.mdx
new file mode 100644
index 00000000..7b16b620
--- /dev/null
+++ b/src/pages/ibc/getting-started.mdx
@@ -0,0 +1,19 @@
+---
+tags: ["ibc"]
+---
+
+# Getting started
+
+To get started, you need to enable the `stargate` feature of the `cosmwasm-std`
+crate. This will enable additional functionality that is not available on all
+chains, including IBC support.
+
+```toml
+cosmwasm-std = { version = "...", features = ["stargate"] }
+```
+
+---
+
+Notes:
+
+- add reference to core capabilities section