Patterns — overview
LinkWorld gives you many primitives — ctx.kv, ctx.secrets,
ctx.events, ctx.tools, on_inbound, heartbeats, wires, grants,
rooms. Picking the right one for a use case isn’t obvious from the
reference docs alone. This section solves that. Each pattern
takes a real problem (“how do I react to an email”) and shows the
canonical solution, the alternatives you should reject, and why.
Read this first if you’re building anything non-trivial. The reference docs explain what each primitive does; the patterns explain which one to reach for. Most architecture mistakes come from using a primitive that “could work” instead of the one LinkWorld designed for the job.
Problem → pattern
Section titled “Problem → pattern”| If you need to… | Use this pattern |
|---|---|
| Store an API key, OAuth token, signing secret | Storing credentials |
| Have each tenant plug in their own third-party key | BYO API keys |
| Run a multi-step process over hours, days, or weeks | Multi-step workflows |
| React when a tenant receives an email | Reacting to emails |
| Have one app trigger another app’s logic | Cross-app events |
| Reach a service LinkWorld doesn’t ship a skill for | Integrating non-platform services |
| Send more than a handful of emails per day per tenant | Sending email at scale |
Primitive → pattern
Section titled “Primitive → pattern”Going the other direction — if you’ve already reached for a specific primitive and want to sanity-check it’s the right one:
| Primitive | Right for | Wrong for — see instead |
|---|---|---|
ctx.kv | App state, cursors, counters, non-sensitive prefs | API keys → Storing credentials |
ctx.secrets | Credentials, tokens, signing secrets | Public settings → manifest install_settings |
manifest.install_settings | UI-rendered tenant-tunable config | Per-request state → ctx.kv |
task_create | One-shot scheduled actions, simple cron | Multi-step branching → Multi-step workflows |
agents[].heartbeats | Recurring agent routines with judgment | Pure DAGs → workflow primitives |
lifecycle.on_inbound | Channel-inbound (email, WhatsApp, voice, web) | Cross-app events → Cross-app events |
subscribes_to | Cross-app event consumption | Inbound channels → Reacting to emails |
ctx.events.emit | Pub/sub to subscribing apps | Sync RPC → ctx.apps.invoke |
ctx.apps.invoke | Sync cross-app call needing an answer | Fire-and-forget → ctx.events.emit |
ctx.team.delegate | Master agent → specialist in same app | Across apps → ctx.apps.invoke |
ctx.tools.call (scoped) | Platform-mediated third-party services | Provider with API key → BYO API keys |
| HTTP request to third-party API | Service you can’t reach via a scope | Service the platform skills cover → scope catalog |
When you’re not sure
Section titled “When you’re not sure”Three questions to ask in order:
- Does LinkWorld ship a scope for this? Check the scope catalog. If yes, declare the scope and call the tool. Stop here.
- Does the third party expose a normal HTTP API? Then it’s
BYO API keys — tenant
brings the key, your app reads via
ctx.secrets. Stop here. - Is it a web-UI-only service? Then it needs a platform skill, not partner-app code. See Integrating non-platform services for how to request one.
Anti-pattern guarantees
Section titled “Anti-pattern guarantees”If a pattern doc says ❌ DON’T, treat that as load-bearing — those came from real failures. Common offenders:
- Storing API keys in
ctx.kv(the lint will block deploy) - Polling
email_searchinstead of usingon_inbound - Building state machines instead of agent heartbeats
- Calling another app’s HTTP endpoint directly (bypassing
ctx.events.emit/ctx.apps.invoke) - Trying to run Playwright inside the partner-app sandbox
- Sending unbounded outbound mail without a daily cap
If your draft architecture relies on any of the above, stop and read the matching pattern doc before you write more code.
What’s not here yet
Section titled “What’s not here yet”These patterns will land as the platform surfaces stabilize:
- Forms + approval flows (when the Commitments Inbox API stabilizes for partner-app use)
- RAG-backed search inside app UIs (when bridge-side RAG hits GA)
- Multi-domain sending (when platform-managed secondary domains ship)
If your use case isn’t covered above and the reference docs don’t make the answer obvious, open a discussion at github.com/linkworld/linkworld — the gap is on us, not on you.