10 minute Forge analytics guide
Get Forge analytics working in 10 minutes - everything else can wait
What You'll Build
A cheap and cheerful analytics implementation that:
- ✅ Tracks user actions without violating Forge privacy policies
- ✅ Works with any analytics provider (Accoil, Segment, etc.)
- ✅ Can be tested locally without sending real data
- ✅ Protects your "Runs on Atlassian" status by preventing End User Data (EUD) egress
- ✅ Limits PII transmission to keep you compliant
Note: This is the minimal viable implementation. For production apps with high volume or complex requirements, see our Complete Analytics Setup for the full queue-based architecture we recommend.
Prerequisites
- A working Forge app
- An analytics API key (we'll use Accoil in examples)
- 10 minutes
The Essential Pattern
Frontend Events → Backend Resolver → Analytics Provider
↑
Backend Events
Key Rule: Frontend NEVER talks directly to analytics. Everything goes through your backend to prevent End User Data (EUD) transmission, which would jeopardize your "Runs on Atlassian" vendor status.
Step 1: Update Your Manifest (1 minutes)
Add these permissions to manifest.yml
:
permissions:
external:
fetch:
backend:
- address: "in.accoil.com" # or your analytics provider
category: analytics
inScopeEUD: false # CRITICAL: Protects "Runs on Atlassian" status
Step 2: Create the Backend Dispatcher (3 minutes)
Create src/analytics/dispatcher.js
:
import { fetch } from '@forge/api';
// Main function that handles all analytics calls
export const sendAnalytics = async (eventName, userId) => {
// Skip in debug mode
if (process.env.ANALYTICS_DEBUG === 'true') {
console.log(`[Analytics Debug] Would send: ${eventName} for user ${userId}`);
return;
}
// Send to your analytics provider
await fetch('https://in.accoil.com/v1/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: eventName,
user_id: userId,
api_key: process.env.ANALYTICS_API_KEY,
timestamp: Date.now()
})
});
};
Step 3: Add the Resolver (2 minutes)
In your main resolver file (usually src/index.js
):
import { sendAnalytics } from './analytics/dispatcher';
resolver.define('track-event', async ({ payload, context }) => {
const userId = context.accountId; // Atlassian user ID
await sendAnalytics(payload.event, userId);
});
Step 4: Track Events (2 minutes)
Frontend Tracking
Create static/spa/src/analytics.js
:
import { invoke } from '@forge/bridge';
export const track = (eventName) => {
invoke('track-event', { event: eventName });
};
Use it in your React components:
import { track } from './analytics';
// In your component
const handleSearch = (query) => {
if (query.length > 2) {
track('Search Performed');
}
// ... rest of your search logic
};
Backend Tracking
Track directly from your resolvers for server-side events:
// In your main resolver (src/index.js)
import { sendAnalytics } from './analytics/dispatcher';
resolver.define('create-todo', async ({ payload, context }) => {
// Your business logic
const todo = await createTodo(payload);
// Track the backend event
const userId = context.accountId;
await sendAnalytics('Todo Created', userId);
return todo;
});
Step 5: Configure and Test (2 minute)
Set your API key:
forge variables set ANALYTICS_API_KEY your_key_here
Enable debug mode for testing:
forge variables set ANALYTICS_DEBUG true
Deploy and test:
forge deploy
forge logs --tail
You should see:
[Analytics Debug] Would send: Search Performed for user 123456789
[Analytics Debug] Would send: Todo Created for user 123456789
That's It! 🎉
You now have working, analytics tracking both frontend interactions and backend operations. Everything else is optimization.
What's Next?
Need to track more events? Focus on meaningful business events:
- Frontend: Call
track('Search Performed')
ortrack('Filter Applied')
- Backend: Call
sendAnalytics('Todo Created', userId)
orsendAnalytics('User Invited', userId)
Want to reduce costs / GDPR compliance? Use the account ID instead of user ID:
const userId = context.cloudId; // Instance ID instead of user ID
Ready for production? Turn off debug mode:
forge variables unset ANALYTICS_DEBUG
Common Gotchas
- ❌ Don't send user emails, names, or any PII
- ❌ Don't track from frontend without going through resolver
- ❌ Don't forget to set
inScopeEUD: false
in manifest - ❌ Don't track generic interactions like "clicked" or "viewed" - focus on business outcomes
Time to first event: 10 minutes ⏱️
Updated 13 days ago
Ready to dive deeper? Check out: