Skip to content

Wires & grants

By default, apps in your tenant don’t talk to each other. They share a database and a user account, but cross-app calls are blocked. This is intentional — your apps are independent products that you’ve installed deliberately, and it’s your decision when (and how) two of them collaborate.

The platform has two contracts for that decision:

  • Grants — caller app may directly invoke specific agents in the callee app
  • Wires — emitter app’s named events fan out to subscriber apps’ agents or heartbeats

Both are bilateral: BOTH apps’ admins must approve before any traffic flows. Either side can revoke at any time.

You install Marketing and Sales as separate apps. Marketing’s CMO agent occasionally wants to ask Sales’ BDR a question — “What’s the status of the Acme deal?”. For that direct question-and-answer pattern, you create a grant from Marketing to Sales for the bdr agent.

Once active, Marketing’s CMO can call ctx.apps.invoke('sales', 'bdr', "status of Acme deal?") from its agentic loop and get a real reply. The platform validates the grant on every call.

Same scenario, different shape. When Marketing finishes qualifying a lead it emits an event called lead_qualified. You want Sales to automatically wake up its BDR with the new lead’s context — no human in the loop, no Marketing-side coordination.

That’s a wire: Marketing emits lead_qualified, Sales subscribes, the platform fans out the event payload to Sales’ BDR.

Wires can also target heartbeats: instead of invoking an agent, the event simply wakes the named heartbeat (sets next_run = now()) so the regular pre-filter + invoke flow runs immediately. Useful when the receiving side already has a heartbeat doing the right thing on its own schedule and you just want to hurry it.

In Workspace Control → Wires & Grants, you see two lists:

  • Active grants — both sides approved, traffic flows
  • Pending grants — one side approved, waiting on the other
  • Active wires — same idea
  • Pending wires — same idea

Each row shows:

  • Caller / callee apps (or emitter / subscriber for wires)
  • Allowed agents (grants) or event name + target (wires)
  • Approval timestamps for each side
  • Rationale (free-text — useful for “why was this approved?” later)
  • Revoke button (always works, both sides)
  1. Click + New grant.
  2. Caller app: pick from the dropdown — the app whose agents want to call out.
  3. Callee app: pick the receiving app.
  4. Allowed agents: list of agent slugs in the callee app that the caller may invoke. Start with one — you can always add more.
  5. Rationale: short text, e.g. “Marketing’s CMO needs occasional pipeline visibility from Sales’ BDR”. This shows up in the audit later if anyone asks why this grant exists.
  6. Save. Your side (the initiator) is auto-approved.
  7. The other side’s admin sees a pending grant and clicks Approve. Traffic starts the moment both approvals exist.
  1. Click + New wire.
  2. Emitter app + event name: which app emits which event. The emitter app’s manifest declares its emits: [...] list — the dropdown is constrained to those.
  3. Subscriber app + kind + target:
    • kind = agent → invoke a specific agent slug on event arrival
    • kind = heartbeat → wake a specific heartbeat
  4. Rationale.
  5. Save. Initiator side auto-approved, other side approves to activate.

Either side can revoke. The grant or wire is deleted (no soft state). New traffic is denied immediately. In-flight calls finish unaffected.

If the same need comes back later, just create a fresh grant / wire — there’s no archive of historical grants in the UI (the audit log records the original approve + the revoke).

GrantWire
TriggerCaller-initiated explicit callEvent emission
DirectionSynchronous request → replyAsynchronous fan-out
Use when”I want to ask the other app something now""When I do X, the other app should react”
Multiple subscribers?No (one caller, one callee)Yes (one event, many subscribers)
Carrying payload?The promptThe event payload (free-form JSON)

Three layers protect cross-app traffic:

  1. Manifest declarations — both apps must declare what they intend to emit / subscribe to / call. Manifests are validated at install.
  2. Bilateral admin approval — your tenant admin (you) signs off explicitly per contract.
  3. Server-side enforcement — every call goes through apps.grants.check_grant (or wire fan-out) before any side- effects. There’s no client-side bypass.

The grant’s allowed_agents list is callee-controlled — the callee admin tightens which of their agents are exposed even within an approved grant.

If two apps are tightly coupled — one is essentially a sub-component of the other — they should probably be one app with multiple agents instead. Cross-app grants exist for inter-department collaboration, not for splitting tightly-coupled logic across deployment boundaries.

If you find yourself creating 10 grants between the same pair of apps, consider whether they’re really one app pretending to be two.

  • Steward — the conversational workspace controller uses these grants to execute cross-app calls on your behalf.
  • Group rooms — when multiple agents need to collaborate over time, a room is often better than a chain of grants.
  • For developers: Cross-app calls reference covers the SDK side (ctx.apps.invoke, ctx.events.emit).