Request Details

I 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.
  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).

  1. Send by inserting a row. Leave msg_id at 0; the runtime assigns it on commit. If the transaction rolls back, nothing is sent.
  #[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,
      });
  }
  1. (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.
  #[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 via the SpacetimeDB site.

  1. website-features added the feature-request label Jun 3, 2026

Reopen this request?

The request will return to its prior live status.

Mark as Duplicate

Pick the request that Async Inter-database Communication (Async IDC) is a duplicate of. Energy rolls up into that request and GitHub closes the linked issue as a duplicate with a timeline reference to the canonical.

Unmark Duplicate

Restore as an independent request. The linked GitHub issue will be reopened and its duplicate-of relationship cleared. The original timeline entries stay as history; the note below is posted as a follow-up comment.