Skip to main content

Self-Report

Self-Report allows members to manually log facility visits when automatic detection (geofencing or beacon proximity) was not possible -- for example, when the member forgot to enable location services or visited a facility without beacons.


Data Model

SelfReportData

Returned by get(), this contains the member's facilities eligible for self-reporting and their past self-report history.

FieldTypeDescription
facilitiesList<Facility>Facilities the member can self-report against.
selfReportMonthsList<SelfReportMonth>Monthly breakdown of past self-reports.
statusMessageString?Optional server message (e.g., daily limit reached).

Each SelfReportVisit references its facility by facilityId only -- resolve display details by matching that id against SelfReportData.facilities.

SelfReportMonth

One calendar month of self-reported visits, grouped so you can render them onto a per-month calendar.

FieldTypeDescription
monthInt?1-based month number (1 = January, 12 = December), or null when omitted.
yearInt?Four-digit year, or null when omitted.
visitsList<SelfReportVisit>The self-reported visits that fall within this month.

SelfReportVisit

A single self-reported visit. Use these fields to drive day-level blocking in the UI (see Product Constraints).

FieldTypeDescription
dayInt?1-based day of the month (1--31), or null when omitted. Combine with the parent SelfReportMonth's month/year to form the full date.
statusTypeActivityStatusReview status of the visit -- typically PENDING, APPROVED/ACCEPTED, or REJECTED. Defaults to PENDING.
facilityIdInt?The reported facility's id, matched against SelfReportData.facilities.

A consumer determines whether a given day already has a blocking report by scanning selfReportMonths[].visits[] for a visit whose day (plus the month's month/year) matches the target date, then inspecting its statusType.


Get Self-Report Data

Retrieve the member's eligible facilities and self-report history.

lifecycleScope.launch {
val data = AFCore.selfReport().get()
val facilities = data.facilities
val history = data.selfReportMonths
// Populate a facility picker and display past reports
}

Submit a Self-Report

Log a manual visit for a specific facility.

Parameters

ParameterTypeRequiredDescription
facilityIdIntYesThe facility where the visit occurred.
timestampInSecondsLongYesUTC epoch timestamp of the visit.
timeSpentInMinutesIntYesDuration of the visit in minutes.
latitudeDouble?NoDevice latitude at time of report.
longitudeDouble?NoDevice longitude at time of report.
atFacilityBooleanNoWhether the member is currently at the facility (default false).
lifecycleScope.launch {
val result = AFCore.selfReport().submit(
facilityId = 42,
timestampInSeconds = System.currentTimeMillis() / 1000,
timeSpentInMinutes = 60,
latitude = 32.7157,
longitude = -117.1611,
atFacility = true
)

if (result.status) {
showSuccess("Visit reported successfully")
} else {
showError("Could not submit report: ${result.statusMessage}")
}
}
Offline behavior

submit is offline-aware. When the device has no connectivity, the report is persisted to the SDK's offline outbox and delivered automatically once the network returns -- no manual retry needed. In that case the returned AFResult has status == true and queued == true. Read result.queued if you want to tell the member their report was "queued for later delivery" versus "submitted online."


Delete a Self-Report

Remove a previously submitted self-report for a given date.

lifecycleScope.launch {
val result = AFCore.selfReport().delete("2026-02-13")
if (result.status) {
showSuccess("Report deleted")
}
}

Product Constraints

Self-reporting is gated per day. Enforce these rules in your UI before showing the submit affordance for a given date:

  • An approved (or partial) non-walking visit blocks the day. If the member already has an approved or partially-approved visit that is not a SmartWalking entry on that day, new self-reports for that day are not allowed.
  • A pending self-report blocks new ones. While a self-report for a day is still PENDING, the member cannot submit another for that day -- they must delete the pending one first.
  • A SmartWalking-only day does NOT block. A day whose only activity is SmartWalking step credit remains open for self-reporting.

You can derive day-level blocking from the data returned by get(): scan each SelfReportMonth.visits for a visit matching the target day, and treat it as blocking when its statusType is PENDING, APPROVED, or ACCEPTED.

lifecycleScope.launch {
val data = AFCore.selfReport().get()

fun isDayBlocked(year: Int, month: Int, day: Int): Boolean =
data.selfReportMonths
.filter { it.year == year && it.month == month }
.flatMap { it.visits }
.any { visit ->
visit.day == day && visit.statusType in setOf(
ActivityStatus.PENDING,
ActivityStatus.APPROVED,
ActivityStatus.ACCEPTED
)
}
}

Best Practices

  • Confirm before submitting. Show a confirmation dialog with the facility name, date, and duration before calling submit.
  • Distinguish manual from automatic visits in the UI. Use different icons or labels so members understand which visits were detected automatically and which were self-reported.
  • Respect server-side limits. Some organizations limit the number of self-reports per day or month. Check statusMessage for guidance.

Quick Reference

AFCore.selfReport().get()
AFCore.selfReport().submit(facilityId, timestampInSeconds, timeSpentInMinutes, lat, lng, atFacility)
AFCore.selfReport().delete(date)