Async Inter-database Communication (Async IDC)
RequestedJun 3, 2026I would like to be able to send messages from one database to another via an outbox table.
Sender side (inventory module)
1. Declare the outbox table. It needs a #[primary_key] #[auto_inc] u64 msg_id, a #[target] typed identity, and payload columns matched by name to the receiver reducer's parameters.
```rs
use game_world as gw;
#[spacetimedb::table(name = out_receive_item, outbox(gw::receive_item))]
pub struct OutReceiveItem {
#[primary_key]
#[auto_inc]
msg_id: u64, // runtime-assigned sequence number
#[target]
target: gw::Identity, // which database to deliver to
// Payload columns — names must match gw::receive_item's parameters.
item_id: u64,
qty: u32,
}
```
Defaults are the strong guarantees: ordered = true, reliable = true → ordered, exactly-once delivery per (sender, receiver).
2. Send by inserting a row. Leave msg_id at 0; the runtime assigns it on commit. If the transaction rolls back, nothing is sent.
```rs
#[spacetimedb::reducer]
fn transfer_item(ctx: &ReducerContext, to: gw::Identity, item_id: u64, qty: u32) {
ctx.db.out_receive_item().insert(OutReceiveItem {
msg_id: 0,
target: to,
item_id,
qty,
});
}
```
3. (Optional) Observe the outcome. Attach an on_result reactive reducer naming the outbox table. It fires once per terminated delivery, post-commit, on the sender. Ok(()) means the
receiver returned Ok; Err(s) carries the receiver's error or a terminal transport/schema failure.
```rs
#[spacetimedb::reducer(on_result(out_receive_item))]
fn on_transfer_result(
ctx: &ReducerContext,
row: OutReceiveItem,
result: Result<(), String>,
) {
if let Err(err) = result {
log::warn!("transfer of item {} to {:?} failed: {}", row.item_id, row.target, err);
}
}
```
_Requested by [@cloutiertyler](https://spacetimedb.com/@cloutiertyler) via the SpacetimeDB site._