Activity log: moving your reporting to the new OData feed

early access.png

This feature is currently available to a limited number of early-access customers. 
We’ll share further updates once we’re ready to widen availability.

Aptem has a new OData feed for logged activity. If you report on off-the-job training (OTJT) hours, this is the feed to build against. This article covers what it contains, how the fields you use today map across, and the traps to avoid on the way.

  • Who this is for: administrators who build or maintain reports from Aptem OData feeds, for example in Power BI or Excel. If nobody at your organisation reports directly from OData, you can skip this article.

On this page

  1. The feed
    1. Access
  2. Why move
  3. What the feed contains
    1. The coded values
  4. Mapping from the evidence feed with activity data
    1. Fields that carry over under a new name
    2. Fields with no equivalent
    3. Reporting by programme
  5. Your history is included
  6. Reconciling your old and new figures
  7. Before you build
  8. Paging and performance
  9. Feeds being retired
    1. Coming in a future release: changes to the standard evidence feeds
  10. If the numbers do not look right

Note: the feeds you use today are deprecated and will eventually be withdrawn. They keep working during the transition, and we will give you notice before any of them are withdrawn.

The feed

/odata/1.0/ActivityLog on your own tenant, for example https://yourtenant.aptem.co.uk/odata/1.0/ActivityLog.

It sits on the same OData service, with the same authentication, as the feeds you already use, so nothing about how you connect changes. One row is one logged activity.

A worked example, returning one learner's activity from the start of the year:

/odata/1.0/ActivityLog?$filter=LearnerId eq 12345 and DateOfActivity ge 2026-01-01T00:00:00Z

Access

Reading the feed requires the Allow tutor to read and write Learner's Activity Logs permission, which every basic admin role holds by default. An account that can already read the evidence feeds will normally be able to read this one.

As with the evidence feeds, you only get rows for the learners that account is allowed to see, so two accounts can legitimately return different totals. Deleted activities are excluded.

Why move

Once you opt in, your activity log data will no longer appear in the evidence feed. The activity log feed provides detailed records captured through the activity log. This is the feed you should use.

Which feed you read today determines what you will see.

  • LearningPlanEvidence and UserEvidences, the standard evidence feeds, will not carry activity log data at any point. Time logged against an evidence item on a programme that is not enabled for activity log continues to appear here exactly as it does now.
  • LearningPlanEvidenceWithActivityData and UserEvidencesWithActivityData, added in February 2026 for early access customers, return evidence and activity log data together. They stay up to date until they are withdrawn.

If you report from the standard evidence feeds, hours logged through the activity log will be missing once a programme is enabled, and your totals will under-count with no error to warn you.

If you report from the feeds with activity data, the hours are there, but they arrive as activity records rather than as ordinary evidence. Any report that filters on evidence-specific fields (evidence kind, evidence name, or the presence of an attachment) will miss them. Those fields are empty or carry no meaning on an activity row.

Both WithActivityData feeds are deprecated and will be withdrawn once customers have moved across, so reporting built on them stops working eventually.

Reading the hours from ActivityLog is the durable fix. It also gives you a cleaner dataset: activity records only, so no evidence rows that carry no recorded time, and no working around the evidence workflow to report on hours.

The feed also carries time from programmes you have not yet enabled. You do not need to build a hybrid model that reads hours from two places depending on a programme's status.

What the feed contains

Field Type What it holds
Id int Unique identifier for the activity. Use this as your key.
LearnerId int The learner the time belongs to. Joins to the Users feed.
ComponentId int The learning plan component the time was logged against.
ActivityType enum (text) The kind of hours: off-the-job training, English and maths, or other.
Description string The description of the activity.
Status enum (text) Where the activity is in the review cycle.
DateOfActivity datetime The date the activity took place. Date only, see below.
TotalMinutes int Time logged, in minutes. Divide by 60 for hours.
HoursType enum (text) When the time took place. This is not the same as HoursType on the evidence feeds, see the warning below.
Source enum (text) Where the activity came from.
RejectionReason string Why the activity was rejected, where it was. Empty otherwise.
ExternalSourceId string For activities generated from something else, the identifier of that thing. Empty for time logged directly.

The coded values

Every coded field returns text, not a number. The value is the name of the option, so Status returns Submitted rather than 0. In the OData metadata these fields appear as enum types, for example Aptem.ActivityLogging.Enums.ActivityType.

The names are fixed and case-sensitive. Match on them exactly, and group any value your report does not recognise rather than dropping the row, so an option added later does not silently remove time from your totals.

ActivityType

Value Meaning
Otjt Off-the-job training
EnglishMaths English and maths
Other Anything that is neither of the above

Status

Value Meaning
Submitted Logged and awaiting review
Accepted Approved. Counts towards off-the-job totals
Rejected Not approved. RejectionReason explains why
Resubmitted Amended after rejection and awaiting review again
Draft Set by the platform when time spent is automatically generated by SCORM assets, placements and workshops, and Teams meetings.

You will not normally see Draft on activity a learner has entered. It appears when activity is logged automatically from SCORM content or a placement and still needs the learner to complete it, and when an activity is reverted because the learning plan component it was logged against has been deleted. In both cases the time does not count until the activity is submitted and accepted.

HoursType

Value Meaning
None Not recorded
DuringWorkingHours During working hours
DuringPaidOwnHours Own time, being paid
DuringToilOwnHours Own time, receiving TOIL (time off in lieu)
DuringUnpaidOwnHours Own time, unpaid

Where no hours type was recorded, HoursType returns None as text, not an empty value. A report that tests for a blank or missing value will not catch these rows.

Source

Value Meaning
ActivityLog Logged in the activity log
Scorm From a SCORM asset
Placement From a placement
Evidence Carried over from evidence

A fifth value, TeamsMeeting, is planned for a future release, for time that comes from a meeting held in Microsoft Teams. Build your report to group unrecognised Source values rather than discard them, so the new value does not drop time from your totals when it arrives.

Mapping from the evidence feed with activity data

This mapping is from LearningPlanEvidenceWithActivityData. That feed is the standard LearningPlanEvidence feed plus five columns: IsActivityLog, ActivityLogId, Description, RejectionReason and Status. If you report from LearningPlanEvidence, those five are not available to you and the rest of the mapping still applies.

Fields that carry over under a new name

On the evidence feed On the activity log feed Note
ActivityLogId Id Was empty on ordinary evidence rows. Every row now has one.
SpentTime TotalMinutes Same meaning, in minutes, new name.
CompletedDate DateOfActivity Same meaning. Date only on the new feed.
SpentTimeType HoursType Same meaning, new name. Values are the names listed above.
HoursType ActivityType Same meaning, new name. Read the warning below.
Status Status Still returned as text, and the values match those on ActivityLog. This column carries across without change.
IsActivityLog not needed Every row in this feed is an activity.
LearnerId, ComponentId, Description, RejectionReason unchanged Same names, same meanings.

Careful with HoursType. This field name exists in both feeds but has a different meaning in each.

On the evidence feed, HoursType is the kind of hours: off-the-job training, English and maths, or other. On the activity log feed that is ActivityType.

On the activity log feed, HoursType is when the time took place: during working hours, or in the learner's own time. On the evidence feed that is SpentTimeType.

A report that carries the name across unchanged will run, return plausible numbers, and be wrong. Map by meaning, not by name.

Fields with no equivalent

Field What to do instead
ProgramId, SubProgramId Join through the learning plan components feed, see below.
SubmittedById Not carried. The feed records the learner the time belongs to, not the account that entered it.
SubmissionDate Not carried. Use DateOfActivity, which is when the activity happened rather than when it was entered.
UpdatedDate Not carried. An incremental refresh keyed on a last-modified date needs rebuilding, most simply as a DateOfActivity window.
EvidenceName Not carried. The nearest equivalent is Description.
EvidenceKind Not needed. Source tells you where an activity came from.
ConfirmedStatus, LatestStatus Replaced by the single Status field.

UpdatedDate and SubmittedById are the two most likely to affect an existing Power BI model. Check for them before you start.

Reporting by programme

The activity log feed identifies the learning plan component but not the programme. To report by programme, join ActivityLog.ComponentId to the Id field of the learning plan components feed, which carries ProgramId and SubProgramId.

Join to Id. The field called ComponentId in the learning plan components feed is a different identifier and will not match.

Your history is included

Your existing time records are moved into the activity log, so the feed carries history as well as new activity. Aptem runs this for you, tenant by tenant, and checks the totals before switching any Aptem reporting over. You do not need to do anything to trigger it.

Moved records carry Source Evidence, and ExternalSourceId holds the identifier of the original evidence record, so you can trace any row back.

A small number of historical records are not moved in the first pass: records with no hours, no learning plan component, no hours type, or no record of when the time took place, and records belonging to learners whose data has been anonymised. Nothing is deleted, and these are being handled separately.

Reconciling your old and new figures

Run both side by side for a period you already trust before you retire the old report. You can expect small differences, and check these three causes as potential reasons:

  1. Held-back records. The historical records described above are not in the feed yet.
  2. Learner visibility. The feed returns only the learners the reporting account can see, which may differ from the account behind your old report.
  3. The HoursType swap. If your totals split by hours type look wrong rather than merely different, this is almost always the cause.

Before you build

  • DateOfActivity carries no meaningful time. Every row reads midnight UTC. An hours-per-day or shift-level view cannot be built from this field.
  • Draft activities do not count towards off-the-job totals. Only accepted time counts. Automatically generated activity arrives as a draft for the learner to complete, so it counts once completed and accepted, not when it was generated.
  • Hours can arrive without a learner typing them in. Time can reach the activity log from SCORM content or from a placement as well as by direct entry. Compliance reporting that assumes learner-entered hours will mis-count.
  • On programmes not yet enabled, everything is labelled as coming from evidence. Automatically generated time on those programmes carries Source Evidence, not Scorm or Placement. You can only distinguish learner-entered from generated time on enabled programmes.

Paging and performance

A maximum page size applies, 5,000 rows per request by default. A query with no $top returns the first 5,000 rows with a total count. A $top larger than the limit will be rejected with an explanatory error.

  • Build paging in from the start.
  • Filter on DateOfActivity and LearnerId server-side.
  • Import only the tables and columns your report uses.

The OData API performance guide applies unchanged.

Feeds being retired

Two feeds are deprecated. Both were introduced in February 2026 for early access customers and return evidence and activity data together.

Feed What is happening
/odata/1.0/LearningPlanEvidenceWithActivityData Deprecated. Move to /odata/1.0/ActivityLog.
UserEvidencesWithActivityData (via the Users expansion) Deprecated. Move to /odata/1.0/ActivityLog.

Both still work and will keep returning evidence and activity log data up to date. They will be withdrawn once customers have moved across, and we will give you notice before that happens.

Coming in a future release – changes to the standard evidence feeds

LearningPlanEvidence and UserEvidences are not changing in this release, and will not carry activity log data at any point. Some columns within them will be withdrawn in a future release, once customers have moved across:

  • On LearningPlanEvidence: SpentTime, SpentTimeType and HoursType.
  • On UserEvidences: HoursType.

No date has been set and we will give notice well in advance. If your hours reporting has already moved to /odata/1.0/ActivityLog, no further action will be needed.

If the numbers do not look right

Get in touch with your Aptem contact before changing anything. It helps if you send your tenant, the feed and query you are using, and the two figures that disagree.

Was this article helpful?
0 out of 0 found this helpful