The problem we kept running into
Every day, multiple Netskope employees would post in our internal #customerzero Slack channel asking for Enterprise Browser (EB) access. The messages were nearly identical: someone had just heard about EB, wanted to try it out, and needed a license key or enrollment invite to get started.
The CZ team would then manually look up the user, send an enrollment invite through the Netskope admin console, and reply in the thread. This worked, but it was repetitive, manual work that pulled our team away from higher-value investigations and dogfooding activities.
We started asking ourselves: why are we the bottleneck here? The process was entirely mechanical — receive request, identify user, send invite, confirm. It was a perfect candidate for automation.
📊 By the numbers
After analyzing 200+ posts in #customerzero, Enterprise Browser invite/license key requests were the second most common issue category — 16+ posts, fully repeatable, zero judgment required to resolve.
What we built
We built a Slack bot — CZ Bot — that automatically detects Enterprise Browser invite requests in #customerzero, pulls the requesting user's email from their Slack profile, and calls the Netskope EB enrollment API to send the invite. The entire flow takes under 5 seconds from message to confirmation.
✅ Before vs After
Before: User posts → CZ team sees it → manually looks up email → opens admin console → sends invite → replies in Slack. Time: 15–60 minutes depending on team availability.After: User posts → CZ Bot detects it → pulls email from Slack → calls Netskope API → invite sent → bot confirms in thread. Time: under 5 seconds. Zero human involvement.
How it works — user experience
From the user's perspective, the experience is simple. They post a message in #customerzero mentioning they need an Enterprise Browser invite or license key. Within seconds, CZ Bot replies in the thread confirming the invite has been sent to their Netskope email address.
They don't need to open a ticket, DM anyone, or wait for business hours. The bot handles it instantly, 24/7.
Example interaction:
👤 Madhura Sridhar • 10:42 AM
Hi Team, I'm having issue with my EB — need a license key to get started.
🤖 CZ Bot • 10:42 AM
Hi! ✅ Done — EB enrollment invite sent to msridhar@netskope.com. Check your inbox 📧
If you don't receive it within a few minutes, check your spam or reply here and we'll re-send.
The tech behind it
We built this using a combination of Slack's Events API, Tines (our security automation platform), and the Netskope Enterprise Browser enrollment API. Here's the architecture at a high level:
| Component | Role |
| Slack Events API | Delivers new messages from #customerzero to our automation in real time |
| Tines Cloud | Public-facing entry point — receives the Slack event, handles verification, and filters for EB invite requests |
| Tines On-Prem | Processes the event behind our Netskope Private Access layer — fetches the user's email and calls the EB API |
| Netskope EB Enrollment API | Sends the enrollment invite directly to the user's email via POST /api/v2/nsbrowser/invite |
| Slack API (chat.postMessage) | Posts the confirmation reply back to the thread |
Edge cases we handled
Building this taught us a few things about real-world Slack automation:
- Third-party requests: Sometimes users ask for an invite on behalf of a colleague (e.g. 'can you send an EB invite to @Francisco Mena'). We detect @mentions in the message and send the invite to the mentioned user's email instead of the sender's.
- Bot loop prevention: Without filtering, the bot would respond to its own replies, creating an infinite loop. We added a bot_id null check to ignore all bot-generated messages.
- Thread reply filtering: The bot only responds to top-level channel messages, not replies within existing threads.
- API failure fallback: If the Netskope enrollment API call fails, the bot posts a fallback message tagging the CZ team to handle it manually.
- Private channels: We extended the bot to work in private Slack channels by adding the groups:history and message.groups scopes.
What's next
This is just the first automation in what we're building out as a broader CZ support bot. The next phase will add a generic triage response for all other NS Client issues — prompting users to share their OS version, NS Client version, and screenshots to help the team investigate faster.
We're also exploring extending this pattern to other repetitive request types in the channel. If you're a Netskope customer interested in replicating this for your own internal support channels, reach out to your Netskope account team — we're happy to share the implementation details.
HIGH-LEVEL LOGIC
CZ Bot — EB Invite Automation
Architecture & flow reference for internal and customer documentation
System architecture
The bot uses a two-tier Tines architecture to bridge the public internet (Slack) with Netskope's internal on-prem systems:
Slack (#customerzero)
↓ Events API (message.channels / message.groups)
Tines Cloud (frosty-hill-7339.tines.com)
[ Webhook → Filter EB Invite Messages → Forward to On-Prem ]
↓ HTTPS tunnel via Netskope Private Access
Tines On-Prem (frosty-mountain.netskope.io)
[ Check for Mention → Get User Email → Send EB Invite → Post Reply ]
↓ Slack API (chat.postMessage) ↓ Netskope EB API
Step-by-step flow
| 1 | Message received User posts a message in #customerzero (or any channel the bot is invited to). Slack delivers the event to Tines Cloud via the Events API. |
| 2 | Filter: real messages only Tines Cloud checks that the message is a top-level post (not a thread reply), not from a bot, and of type 'message'. Thread replies and bot messages are discarded. |
| 3 | Filter: EB invite keywords The message text is checked against a keyword list (e.g. 'license key', 'enterprise browser', 'EB invite', 'enroll'). If no keywords match, the message is forwarded to the generic response flow instead. |
| 4 | Forward to On-Prem Tines Cloud forwards the Slack event payload to the on-prem Tines instance via an HTTPS tunnel through Netskope Private Access. |
| 5 | Check for @mention On-prem checks whether the message contains an @mention of another user using regex. If yes, the invite goes to the mentioned user. If no, it goes to the message sender. |
| 6 | Fetch user email Tines On-Prem calls the Slack API (users.info) to retrieve the target user's email address from their Slack profile. |
| 7 | Send EB enrollment invite Tines On-Prem calls the Netskope EB enrollment API (POST /api/v2/nsbrowser/invite) with the user's email. The API sends the enrollment email directly to the user. |
| 8 | Post confirmation Tines On-Prem calls the Slack API (chat.postMessage) to post a confirmation reply in the original message thread, confirming the invite was sent. |
| 9 | Fallback on error If the Netskope API call fails, the bot posts a fallback message in the thread tagging the CZ team to handle it manually. |
Key decision points
| Decision point | If true | If false |
| Is message a top-level post? | Continue processing | Discard — no response |
| Is message from a bot? | Discard — no response | Continue processing |
| Does message match EB keywords? | Route to EB invite flow | Route to generic response flow |
| Does message contain @mention? | Send invite to mentioned user | Send invite to message sender |
| Did Netskope API succeed? | Post confirmation in thread | Post fallback, tag CZ team |
Netskope EB Enrollment API
The bot uses the following API call to send enrollment invites:
POST https://<tenant>.goskope.com/api/v2/nsbrowser/invite
Headers:
Authorization: Bearer <token>
Content-Type: application/json
Body:
{
"invitation": {
"userIds": ["user@netskope.com"]
},
"sendInvitationEmail": true,
"emailTemplate": 0
}Scopes and permissions required
| Scope | Type | Purpose |
| channels:history | Slack Bot | Read messages from public channels |
| groups:history | Slack Bot | Read messages from private channels |
| chat:write | Slack Bot | Post messages to channels |
| users:read | Slack Bot | Fetch user profile information |
| users:read.email | Slack Bot | Fetch user email from Slack profile |
| message.channels | Slack Event | Subscribe to public channel messages |
| message.groups | Slack Event | Subscribe to private channel messages |



