Event signal:user.created| Destination:Slack| Use case:Multi-Environment Workspaces| Typical setup:~20 minutes
Workflow outcome
Three Meshes workspaces — dev, staging, and production — each with its own Slack connection pointing at a different channel and its own publishable key your app uses per environment.
One product emit path that reads the current environment's publishable key from configuration and routes user.created to the right workspace. No branching, no feature flags, no test data bleeding into prod alerts.
Why teams care
Integration code and test data do not mix well. The moment you wire up a production Slack channel or CRM, every dev branch, integration test, and staging deploy becomes a chance to fire off a real side effect by accident.
The cleanest pattern is to treat environments the same way you treat customers: separate workspaces. Each environment has its own publishable key, its own connections, and its own event history.
Because workspaces are the same primitive you use for external customers, onboarding a new environment is the same operation you already automate for tenants — you do not learn two different patterns.
Even when the code behaves identically across environments, the connections behind each workspace point somewhere different, so there is nothing to cross-contaminate.
What it depends on
These pages stay focused on the workflow outcome, but the setup still needs the right workspace, destination connection, and event path underneath.
Create dev, staging, and production workspaces in your Meshes organization. Each will own its own connections and publishable key.
Read moreDecide which Slack channel each environment should post to. Production usually points at a real ops channel; dev and staging usually point at deliberately noisy channels.
Read moreYour code needs a way to load the current environment's publishable key at runtime — usually through environment variables.
Your product needs to emit user.created (or the event you pick) through the publishable-key events client.
The source event
The payload shape is identical across environments. The difference is entirely in which publishable key you pass to the events client — that decides which workspace, and therefore which Slack connection, receives the event.
Event payload
user.created{
"user_id": "usr_homer",
"email": "homer@springfield-npp.dev",
"account_id": "acc_springfield",
"environment": "staging",
"created_at": "2026-04-17T12:00:00Z"
}What matters most
user_id + email
Identifies the user consistently across environments so staging logs look structurally like production logs.
account_id
Keeps tenant context visible in each environment for debugging without crossing the environment boundary.
environment
Optional but useful metadata so the Slack message clearly labels where the event originated, reducing the chance of mistaking a staging alert for a production one.
created_at
Lets each environment correlate its events with its own monitoring timeline.
Field mapping view
| Event field | Destination target | Why it matters |
|---|---|---|
| Slack message body | Same mapping in every environment — only the Slack connection and channel differ. | |
| environment | Slack message header or prefix | Label dev/staging messages visibly so readers never confuse them with production. |
| account_id | Reference block | Keep the reference shape identical across environments so debugging is symmetric. |
| created_at | Timestamp | Same timestamp mapping everywhere so tests behave like production traffic. |
The destination connection
In each environment workspace, create a Slack connection that points at a channel appropriate for that environment. Dev and staging usually connect to deliberately noisy channels; production connects to the real ops channel. The rule shape is the same in all three workspaces — only the connection changes.
Where Meshes matters
Most teams do not need another destination. They need the destination to stay in sync without embedding its delivery quirks, retries, and mapping logic into the product code path.
Author the same rule in each workspace: bind user.created to a Slack Send Message action targeting the per-environment connection. Because the rule shape is identical, you can script rule creation against the management API and apply it to every environment workspace the same way.
A sample event
This is the part teams like: the source event stays readable and product-shaped while Meshes owns the destination-facing complexity.
TypeScript example
import MeshesEventsClient from '@mesheshq/events';
// WORKSPACE_PUBLISHABLE_KEY is set per environment:
// dev → pk_dev_... → dev workspace → #dev-alerts
// stage → pk_stage_... → staging workspace → #staging-alerts
// prod → pk_prod_... → production workspace → #alerts
const events = new MeshesEventsClient(
process.env.WORKSPACE_PUBLISHABLE_KEY!,
);
await events.emit({
event: 'user.created',
payload: {
user_id: 'usr_homer',
email: 'homer@springfield-npp.dev',
account_id: 'acc_springfield',
environment: process.env.APP_ENV,
created_at: new Date().toISOString(),
},
});Destination outcome
Run the emit path from each environment and confirm the message lands in that environment's Slack channel — and only in that channel. Try it from dev first, then staging, then production.
Operational visibility
The difference between a nice demo and a usable product workflow is whether you can see what happened when the destination is slow, misconfigured, or unavailable.
In Meshes
Why teams buy Meshes
What's next
Use Case
See the broader environment-isolation pattern this guide implements.
Open linkDocs
Spin up a workspace and grab a publishable key for each environment.
Open linkDocs
See the publishable-key events pattern that lets the same code target different workspaces.
Open linkDocs
Keep rules consistent across environments while letting each workspace own its connections.
Open linkGuide
See the same workspace primitive applied to external customer tenancy.
Open linkCompare
Compare per-workspace environment isolation to rolling env flags and branch logic in your own integration code.
Open link