> ## Documentation Index
> Fetch the complete documentation index at: https://docs.timeback.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Single-session activities

> Track activities that students complete in one sitting

Single-session activities are the simplest model: a student starts and completes an activity in one browser session. The client SDK tracks time automatically and reports completion when the student finishes.

<Tip>
  For info on shared concepts, see the [Custom Activities
  overview](/beta/build-on-timeback/sdk/activity-tracking/intro).
</Tip>

## How it works

```mermaid theme={null}
graph LR
    subgraph Client
        A["activity.start()"] --> B["Periodic heartbeats"]
        B --> C["activity.end(metrics)"]
    end
    B -- "TimeSpentEvent" --> D["Caliper API"]
    C -- "Final TimeSpentEvent" --> D
    C -- "ActivityCompletedEvent" --> D
    style A fill:#3b82f6,stroke:#2563eb,color:#fff
    style C fill:#10b981,stroke:#059669,color:#fff
    style D fill:#8b5cf6,stroke:#7c3aed,color:#fff
```

Once started, the SDK sends periodic [TimeSpentEvents](/beta/build-on-timeback/reference/events#timespentevent) throughout the session. When the student finishes, the SDK sends one final [TimeSpentEvent](/beta/build-on-timeback/reference/events#timespentevent) followed by an [ActivityCompletedEvent](/beta/build-on-timeback/reference/events#activitycompletedevent). All events share the same [`runId`](/beta/build-on-timeback/sdk/activity-tracking/intro#event-correlation-with-runid) for correlation.

## Starting an activity

Every single-session activity starts the same way:

```typescript theme={null}
const activity = timeback.activity.start({
    id: 'intro-to-fractions',
    name: 'Introduction to Fractions',
    course: { subject: 'Math', grade: 3 }, // from timeback.config.json
})

// Student engages... (heartbeats emit automatically)
```

<Info>
  See the
  [reference](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-start-params) for
  full parameter documentation including time options, callbacks, and `runId`.
</Info>

## Ending an activity

How you *end* an activity depends on whether the student *completed* the activity. In both cases, accumulated time data is flushed. The difference is whether a completion result is also recorded.

### With completion

When a student finishes, call `activity.end()` with completion metrics:

1. Flushes a final heartbeat ([`TimeSpentEvent`](/beta/build-on-timeback/reference/events#timespentevent))
2. Submits the completion ([`ActivityCompletedEvent`](/beta/build-on-timeback/reference/events#activitycompletedevent))

```typescript theme={null}
await activity.end({
    xpEarned: 8,
    totalQuestions: 10,
    correctQuestions: 8,
    masteredUnits: 1,
})
```

<Info>
  See the [reference](/beta/build-on-timeback/sdk/activity-tracking/reference#activity-end-data)
  for full completion data documentation.
</Info>

### Without completion

Call `activity.end()` without arguments to flush time data only; in this case, no completion event is recorded.

1. Flushes a final heartbeat ([`TimeSpentEvent`](/beta/build-on-timeback/reference/events#timespentevent))
2. Stops the heartbeat timer

```typescript theme={null}
await activity.end()
```

There are two reasons to end without completion:

1. **Cleanup**: the component unmounts before the student finishes (e.g. navigating away mid-activity). You want to flush accumulated time without recording a result.
2. **Stateful activities**: the activity spans multiple sessions, so the client only tracks time per visit. Completion is [recorded by the server](/beta/build-on-timeback/sdk/activity-tracking/stateful) when the student eventually finishes.

## Framework integration

Start tracking after the browser client is initialized and the current user is verified. Use the [React](/beta/build-on-timeback/sdk/client/react), [Vue](/beta/build-on-timeback/sdk/client/vue), [Svelte](/beta/build-on-timeback/sdk/client/svelte), or [Solid](/beta/build-on-timeback/sdk/client/solid) verification API. A client object alone does not establish authentication.

Keep one activity owner for your lesson screen. Await `end()` before starting the next activity, including navigation and development-mode remounts. Async cleanup is not awaited by framework unmount hooks; starting immediately in the next mount can otherwise throw “An activity is already active.”

This example is a browser event handler after initialization and verification. `timeback` is the initialized SDK client and `showLesson` is your app's rendering function:

```typescript theme={null}
async function beginLesson(lessonId: string, lessonName: string) {
    const previous = timeback.activity.current
    if (previous) await previous.end()

    const activity = timeback.activity.start({
        id: lessonId,
        name: lessonName,
        course: { subject: 'Math', grade: 3 },
        onError: (error, context) => console.warn(context.type, error.message),
    })
    showLesson(activity)
}
```

Serialize calls to this handler in your app. If you load lesson data asynchronously, cancel or ignore that load after the screen unmounts, before calling `start()`. Capture the activity created by that screen in its cleanup; end it without metrics. Handle a rejected completion in your UI and let the student retry before starting another activity.

## Best practices

<AccordionGroup>
  <Accordion title="Start activities in useEffect/onMount">
    Start after initialization and user verification, and serialize activity transitions. Register time-only cleanup for the owning screen.
  </Accordion>

  <Accordion title="Flush time on cleanup, complete on user action">
    Use `activity.end()` (no args) in cleanup functions. Only call `activity.end(metrics)` when
    the student has actually finished the activity.
  </Accordion>

  <Accordion title="Handle missing timeback client">
    Check client initialization and [user verification](/beta/build-on-timeback/sdk/identity) separately.
  </Accordion>

  <Accordion title="Use meaningful activity IDs">
    Use stable, unique IDs that identify the specific lesson or content piece.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Stateful activities" icon="arrows-rotate" href="/beta/build-on-timeback/sdk/activity-tracking/stateful">
    Multi-session activities with server-side completion
  </Card>

  <Card title="Reference" icon="code" href="/beta/build-on-timeback/sdk/activity-tracking/reference">
    Parameters, properties, methods, and callbacks
  </Card>
</CardGroup>
