Export training data to Excel via the Jam API
๐Ÿ“Š

Export training data to Excel via the Jam API

Jam's Data Export API lets you pull your training data into Excel for custom reporting. Once set up, refreshing your data takes a single click.

Before you start

You'll need:
  • An Owner role in your Jam organization
To create your API key:
  1. Go to https://auth.wejam.ai/org/api_keys/ and sign in as an Owner.
  1. Click Create key, give it a name, and copy it somewhere safe.
  1. Keep it private โ€” it grants read access to your organization's data. If it's ever exposed, delete it and create a new one from the same page.
๐Ÿ’ก You only need to create the API key once. You'll paste it into Excel during setup, and it stays there for all future refreshes.
For more details you can check: Using the Jam API for Data Export.

What data you can pull

The API covers four main training entities. Others โ€” teams, sprints, track assignments, and mission assignments โ€” are also available and work the same way.
Entity
Endpoint
What it contains
Users
/users
People in your org (name, email, IDs)
Sessions
/sessions
Individual role-play rounds with scores
Tracks
/tracks
Learning paths that group missions
Missions
/missions
Individual training scenarios
๐Ÿ’ก A session is a single role-play round played by one learner on one mission. It's the core performance record โ€” it carries an overall score (0โ€“100) and a per-task scorecard breakdown.

Set up Excel (one time)

Do this once. After setup, refreshing data is just Data โ†’ Refresh All.
  1. In Excel, go to Data โ†’ Get Data โ†’ From Other Sources โ†’ Blank Query.
  1. In the editor that opens, choose Home โ†’ Advanced Editor.
  1. Delete whatever is in the box, paste one of the code examples from the section below, and replace YOUR_API_KEY with your real key.
  1. Click Done.
  1. If Excel asks how to connect to api.wejam.ai, choose Anonymous and click Connect.
  1. When the table preview looks right, click Home โ†’ Close & Load.
โš ๏ธ Always choose Anonymous when Excel asks for credentials โ€” not "Web API" or "Organizational account." Your API key is already included in the code as a header, so no other auth is needed.

Code examples (Power Query)

Two methods below. Method A is quickest for a single entity. Method B is cleaner if you want all four โ€” your key lives in one place and you get four tidy worksheets.

Method A โ€” one query per entity

Paste this into the Advanced Editor. It pulls missions by default. To pull a different entity, change the single word at the end of the BaseUrl line โ€” missions โ†’ users, sessions, or tracks. Everything else stays the same.
let BaseUrl = "https://api.wejam.ai/api/v1/data-exports/missions", ApiKey = "YOUR_API_KEY", GetPage = (page as number) as record => Json.Document( Web.Contents( BaseUrl, [ Query = [ page = Text.From(page), limit = "100" ], Headers = [ #"X-API-KEY" = ApiKey, #"accept" = "application/json" ] ] ) ), Pages = List.Generate( () => [ p = 1, resp = GetPage(1) ], each [resp] <> null, each if [resp][meta][hasNext] = true then [ p = [p] + 1, resp = GetPage([p] + 1) ] else [ p = [p] + 1, resp = null ], each [resp][data] ), Rows = List.Combine(Pages), AsTable = Table.FromList(Rows, Splitter.SplitByNothing(), {"Record"}), Expanded = if List.IsEmpty(Rows) then AsTable else Table.ExpandRecordColumn(AsTable, "Record", Record.FieldNames(Rows{0})) in Expanded

Method B โ€” one reusable function (recommended)

This keeps your API key in a single spot.
Step 1 โ€” store your key as a parameter.
In Power Query, go to Home โ†’ Manage Parameters โ†’ New Parameter. Name it ApiKey, type Text, and set the current value to your key.
Step 2 โ€” create the shared function.
New Source โ†’ Blank Query โ†’ Advanced Editor. Paste the code below and rename the query to fnGetJamData.
(entity as text) as table => let BaseUrl = "https://api.wejam.ai/api/v1/data-exports/", GetPage = (page as number) as record => Json.Document( Web.Contents( BaseUrl & entity, [ Query = [ page = Text.From(page), limit = "100" ], Headers = [ #"X-API-KEY" = ApiKey, #"accept" = "application/json" ] ] ) ), Pages = List.Generate( () => [ p = 1, resp = GetPage(1) ], each [resp] <> null, each if [resp][meta][hasNext] = true then [ p = [p] + 1, resp = GetPage([p] + 1) ] else [ p = [p] + 1, resp = null ], each [resp][data] ), Rows = List.Combine(Pages), AsTable = Table.FromList(Rows, Splitter.SplitByNothing(), {"Record"}), Expanded = if List.IsEmpty(Rows) then AsTable else Table.ExpandRecordColumn(AsTable, "Record", Record.FieldNames(Rows{0})) in Expanded
Step 3 โ€” create one query per entity.
For each entity, do New Source โ†’ Blank Query โ†’ Advanced Editor and enter a single line:
= fnGetJamData("users")
= fnGetJamData("sessions")
= fnGetJamData("tracks")
= fnGetJamData("missions")
Name each query (Users, Sessions, Tracks, Missions) and click Close & Load. You'll get four worksheets, all sharing the same key.

Bonus: a ready-to-read sessions report

Raw sessions reference users and missions by ID. The query below joins them so each row shows the learner's name, mission title, date, score, and completion status โ€” the kind of table you can hand straight to a manager.
This uses the fnGetJamData function from Method B above.
let Sessions = fnGetJamData("sessions"), Users = fnGetJamData("users"), Missions = fnGetJamData("missions"), // Pull the score out of the nested analysis record WithScore = Table.AddColumn( Sessions, "Score", each if [analysis] <> null then Record.FieldOrDefault([analysis], "score", null) else null, Int64.Type ), // Add learner names WithUsers = Table.NestedJoin( WithScore, {"learnerUserId"}, Users, {"id"}, "u", JoinKind.LeftOuter ), AddName = Table.AddColumn( WithUsers, "UserName", each Text.Combine({ [u]{0}?[firstName]?, [u]{0}?[lastName]? }, " ") ), // Add mission titles WithMissions = Table.NestedJoin( AddName, {"missionId"}, Missions, {"id"}, "m", JoinKind.LeftOuter ), AddTitle = Table.AddColumn( WithMissions, "MissionTitle", each [m]{0}?[title]? ), // Keep just the report columns Final = Table.SelectColumns( AddTitle, {"UserName", "MissionTitle", "createdAt", "Score", "completed"} ), Sorted = Table.Sort(Final, {{"createdAt", Order.Descending}}) in Sorted

Key fields by entity

The code auto-expands all fields, but these are the ones most reports rely on.
Users: id ยท firstName ยท lastName ยท email
Sessions:
  • analysis.score โ€” overall score 0โ€“100 (the headline metric)
  • completed โ€” whether the role-play was finished (true/false)
  • createdAt โ€” when the session was played
  • learnerUserId โ€” links to id in Users
  • missionId โ€” links to id in Missions
  • Scorecard: each task has an itemScore (0โ€“6), mapped to solved / partly solved / not yet solved
Tracks & Missions: id ยท title โ€” see the Swagger reference for the full schema

Troubleshooting

Symptom
Fix
Excel keeps asking for credentials
Choose Anonymous. If you picked the wrong type earlier, go to Data โ†’ Get Data โ†’ Data Source Settings, select api.wejam.ai, and clear/edit the permission.
401 Unauthorized error
The key is wrong, expired, or deleted โ€” or it wasn't created by an Owner. Recreate it at the API keys page.
Header not recognized
The header name must be exactly X-API-KEY (case-sensitive).
Table comes back empty
That entity may have no data, or all data fits on page 1 (which is fine โ€” no error).
Need to refresh data
Click Data โ†’ Refresh All. No other steps required.