Send learning data

Record what learners do in your app (events, drafts, ratings and scores) so it joins their learner record and the research export.

2 min read · Updated Oct 29, 2026

VIABLE sees only your app's agent runs. Send the rest from your server: what learners did and wrote, how they rated replies and how they scored. It joins the learners your runs already name.

What you get

  • Insights in Build → Apps: active learners, time writing, pauses, pastes, drafts handed in, ratings and learner groups, day by day.
  • A timeline per learner in Teach → Learners: their work in your app by activity, and their scores.
  • The research export with your app's data (writing.csv measures how each draft was written), ready for Coding.

To have your coding AI wire it up, paste this message in your app's code:

text
Read https://www.viablelab.org/downloads/viable-cli.md and set up VIABLE CLI and its Agent Skill if they are missing, then follow the Skill's "Connect an app to VIABLE" with https://developers.viablelab.org/docs/connect/first-run.md and https://developers.viablelab.org/docs/connect/learning-data.md: make this app's server send user, X-Platform-ID, include: ["trace"] and its learning data, then verify with viable developer apps list and viable developer apps insights <platform-id>.

Before you start

  1. Give the key the scope. In Operate → API keys, choose Edit key on your app's key and turn on Record learning data (learning:write).
  2. Tag every call. Send X-Platform-ID, such as writing-app. Events and drafts are filed under your app's ID and the activity, so they need it.
  3. Reuse your user. The user you send on runs is the same learner here. A new user creates a learner.

Send events

POST /v1/learning/events takes one learner's events in one activity, up to 200 per call. Use VIABLE's event kinds, such as writing.progress (keys, pastes, pauses, active time), writing.snapshot (the text at a pause), writing.submit and panel.view, or app.event for your own, with a flat data object. Each kind keeps only its own fields. The response lists any event it skipped, by index.

js
// On your server, never in the browser.
const res = await fetch('https://api.viablelab.org/v1/learning/events', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.VIABLE_API_KEY}`,
    'Content-Type': 'application/json',
    'X-Platform-ID': 'writing-app'
  },
  body: JSON.stringify({
    user: 'writing-app:7f3a9c21',
    activity: 'persuasive-essay-1',
    events: [{ type: 'writing.progress', at: new Date().toISOString(), data: { field: 'draft', keys: 140, pasted: 0, pauses: 3, active_ms: 95000 } }]
  })
})
if (!res.ok) console.error((await res.json()).error)

Save drafts

POST /v1/learning/drafts saves the draft, and the plan if your app has one, as they are now: send the whole text each time. Add submit: true when the learner hands it in.

Rate replies

When a learner marks a reply helpful or not, send POST /v1/agents/{slug}/runs/{run_id}/rating with rating set to 1, -1, or null to take it back. It uses Run agents and works only for runs this key made.

Send scores

POST /v1/learning/outcomes records one measure, such as a rubric total, for up to 1,000 users. A user that no run, event or draft has named yet comes back in unmatched.

Where it lands

  • Research → Export, with Your apps (API), or one app from its Insights: events in workspace_events.csv, drafts and how they were written in writing.csv, ratings in turns.csv, scores in outcomes.csv. A run with user also keeps its context as the turn's workspace.
  • Build → Apps: the checklist marks each kind your app has sent, and Insights counts it.

Warning

Consent is your app's to handle: tell learners what it records before you send anything. Keep user opaque, as on your runs: never a name or an email.

This page is also plain Markdown at /docs/connect/learning-data.md. Point your AI at /llms.txt to find every page.