Integrate AgileBrain Exercises
Run AgileBrain exercises inside your product with a link, an embedded frame, or a pop-up, and receive each participant's scores on your server by webhook.
Code samples on GitHub →Integration Points hub.agilebrain.com
/run/{your-exercise-slug}Participant link, frame, or pop-upsession.createdSession startedsession.completedScores sent to your serverHow an Integration Works
AgileBrain hosts and scores the exercise. Your product sends participants to it and receives the result.
Set Up the Exercise
AgileBrain sets up an exercise for your organization and sends you its link and deployment ID.
Open the Exercise
Send each participant to your exercise link with a participant ID.
Run and Score
The Hub runs the exercise and scores the session.
Receive Scores
The Hub posts session.completed to your webhook.
Run the Exercise
Use any of the three options below. The Hub runs the full participant experience.
- Add a participant ID to the link as
?sessionId=. The Hub returns it assession.customIdin every webhook. - Generate a new random value, such as a UUID, for each participant and exercise, and map it to the participant on your server. Never use an email address, a name, or a sequential ID.
- Each participant ID can be used for one completed exercise.
- Scores arrive on your server by webhook.
- Give AgileBrain a return URL during onboarding and participants land there when they finish.
- On phones, open the direct link so the exercise has the full screen.
<!-- Direct link --><a href="https://hub.agilebrain.com/run/your-exercise-slug?sessionId=PARTICIPANT_ID">Start the exercise</a> <!-- Embedded frame --><iframesrc="https://hub.agilebrain.com/run/your-exercise-slug?sessionId=PARTICIPANT_ID"width="100%" height="700"style="border: none; border-radius: 8px;"allow="fullscreen"title="AgileBrain exercise"></iframe> <!-- Pop-up window (opens from a click) --><button type="button" onclick="window.open('https://hub.agilebrain.com/run/your-exercise-slug?sessionId=PARTICIPANT_ID','agilebrain', 'width=900,height=760')">Start the exercise</button>Webhook Events
AgileBrain turns on the webhook for your organization during onboarding. The Hub then posts each session event to your server as JSON.
session.createdfires when a session starts and carries no scores.session.completedfires when the session is scored.- One webhook receives events for every exercise in your organization. Filter on
eventandsession.deploymentId. - Keep your receiver available and respond with any 2xx status within 10 seconds. Store the payload and process it after you respond.
- Keep your webhook path secret and accept only participant IDs your server issued.
Scores
needScores maps each score name to a number. Treat the keys as opaque and do not assume a fixed list. Score definitions come with onboarding.
POST https://your-server.example.com/agilebrain/<unguessable-path>Content-Type: application/json {"event": "session.completed","sentAt": "2026-01-15T14:30:00.000Z","session": {"customId": "3f6c1e2a-9b7d-4c1e-8a52-0d4f7b9e6a10","status": "COMPLETED","deploymentId": "REPLACE-WITH-DEPLOYMENT-ID","needScores": {"exampleScore": 0.61,"anotherScore": 0.48}}}Payloads can include more fields than shown, such as participant details your exercise collects. Ignore fields you do not use.
Receive Your First Webhook
This Node.js receiver handles AgileBrain webhooks. It needs Node 18 or later and has no dependencies.
- Save it as
receiver.mjsand runnode receiver.mjs. - Run the test delivery. Use the same path and deployment ID in both files. The receiver prints the participant ID and the scores.
- Set
WEBHOOK_PATHto a random path andDEPLOYMENT_IDto the value from onboarding, then replaceissuedByYouandsaveScoreswith your own lookup and storage. - Give AgileBrain the public HTTPS address of your receiver, including its path.
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' -X POST http://localhost:3000/agilebrain/REPLACE-WITH-RANDOM-PATH \-H "Content-Type: application/json" \-d '{"event":"session.completed","sentAt":"2026-01-15T14:30:00.000Z","session":{"customId":"3f6c1e2a-9b7d-4c1e-8a52-0d4f7b9e6a10","status":"COMPLETED","deploymentId":"REPLACE-WITH-DEPLOYMENT-ID","needScores":{"exampleScore":0.61}}}'import { createServer } from 'node:http';
const PORT = 3000;
const WEBHOOK_PATH = '/agilebrain/REPLACE-WITH-RANDOM-PATH'; // keep secret
const DEPLOYMENT_ID = 'REPLACE-WITH-DEPLOYMENT-ID'; // from onboarding
createServer((req, res) => {
const path = new URL(req.url, 'http://localhost').pathname;
if (req.method !== 'POST' || path !== WEBHOOK_PATH) {
res.writeHead(404).end();
return;
}
let body = '';
req.setEncoding('utf8');
req.on('data', (chunk) => { body += chunk; });
req.on('end', () => {
res.writeHead(204).end(); // acknowledge first, then process
let payload;
try { payload = JSON.parse(body); } catch { return; }
if (!payload || typeof payload !== 'object') return;
const { event, session } = payload;
if (session?.deploymentId !== DEPLOYMENT_ID) {
console.log('Ignored delivery for deployment', session?.deploymentId);
return;
}
if (event === 'session.completed') {
// customId is the participant ID you passed as ?sessionId=
if (!issuedByYou(session.customId)) return;
saveScores(session.customId, session.needScores);
}
});
}).listen(PORT, () => console.log(`Listening on ${PORT}`));
function issuedByYou(participantId) {
return true; // replace with a lookup of the IDs your server issued
}
function saveScores(participantId, scores) {
console.log(participantId, scores); // replace with your storage
}Request Access
AgileBrain grants access to approved partners under an agreement. Leave your details and our team will follow up.
Onboarding Includes
- Your exercise link and deployment ID
- Webhook setup
- Score definitions
