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.
When you want a grant
Section titled “When you want a grant”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.
When you want a wire
Section titled “When you want a wire”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.
The Wires & Grants screen
Section titled “The Wires & Grants screen”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)
Creating a grant
Section titled “Creating a grant”- Click + New grant.
- Caller app: pick from the dropdown — the app whose agents want to call out.
- Callee app: pick the receiving app.
- Allowed agents: list of agent slugs in the callee app that the caller may invoke. Start with one — you can always add more.
- 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.
- Save. Your side (the initiator) is auto-approved.
- The other side’s admin sees a pending grant and clicks Approve. Traffic starts the moment both approvals exist.
Creating a wire
Section titled “Creating a wire”- Click + New wire.
- Emitter app + event name: which app emits which event. The
emitter app’s manifest declares its
emits: [...]list — the dropdown is constrained to those. - Subscriber app + kind + target:
- kind = agent → invoke a specific agent slug on event arrival
- kind = heartbeat → wake a specific heartbeat
- Rationale.
- Save. Initiator side auto-approved, other side approves to activate.
Revoking
Section titled “Revoking”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).
What runs over a grant vs. a wire?
Section titled “What runs over a grant vs. a wire?”| Grant | Wire | |
|---|---|---|
| Trigger | Caller-initiated explicit call | Event emission |
| Direction | Synchronous request → reply | Asynchronous 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 prompt | The event payload (free-form JSON) |
Security model
Section titled “Security model”Three layers protect cross-app traffic:
- Manifest declarations — both apps must declare what they intend to emit / subscribe to / call. Manifests are validated at install.
- Bilateral admin approval — your tenant admin (you) signs off explicitly per contract.
- 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.
When to not create a grant
Section titled “When to not create a 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.
See also
Section titled “See also”- 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).