---
title: Send learning data
description: Record what learners do in your app (events, drafts, ratings and scores) so it joins their learner record and the research export.
updated: 2026-10-29
---

> All VIABLE docs, for people and their agents: https://developers.viablelab.org/llms.txt

# Send learning data

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](/dashboard/apps): active learners, time writing, pauses, pastes, drafts handed in, ratings and learner groups, day by day.
- **A timeline per learner** in [Teach → Learners](/dashboard/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](/docs/research/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

::: steps
1. **Give the key the scope.** In [Operate → API keys](/dashboard/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](/dashboard/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.

::: cards
- [Learners and traces](/docs/connect/learners-and-traces): name your users and keep each run's trace.
- [Export](/docs/research/export): download it all for analysis.
:::
