> ## 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.

# Partner Event Reference

> Fields, actions, and examples for POST /events/1.0

This reference applies to the Partner Events API at `POST /events/1.0`. Send one event per request. An envelope is accepted only when `data` contains exactly one event. The API derives its transport envelope from the event; an outer `sensor` does not override `edApp`.

## Shared fields

| Field                  | Contract                                                                                            |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| `id`                   | Unique event identifier in `urn:uuid:` form; reuse for retries of that event                        |
| `type` and `action`    | A combination from the table below                                                                  |
| `eventTime`            | UTC ISO 8601 timestamp ending in `Z`                                                                |
| `@context`             | Use `http://purl.imsglobal.org/ctx/caliper/v1p2`; the schema supplies this value when omitted       |
| `profile`              | Required for assessment, assignable, and grade events; optional for view and session events         |
| `edApp`                | `SoftwareApplication` with an ID URL whose path is `/applications/1.0/{app_sourced_id}`             |
| Student reference      | `Person` with an ID URL whose path is `/rostering/1.0/users/{user_sourced_id}`                      |
| `extensions.subject`   | Required for the five non-session event types                                                       |
| `extensions.course.id` | Required for grade, assessment, and assessment-item events; optional for assignable and view events |

Use the IDs assigned to you through Timeback's application, roster, and course APIs. In the examples, `api.example.com` and all example IDs are placeholders. Substitute your API base URL and assigned IDs. A bare student ID does not satisfy the URL schema.

The event and many nested objects reject unknown fields. Place only documented extensions in each extension object. `group.id` is not a substitute for `extensions.course.id` in this contract.

## Actions and results

| Type                  | Profile                   | Accepted actions                                                            | Primary meaning                                            |
| --------------------- | ------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `AssessmentEvent`     | `AssessmentProfile`       | `Started`, `Paused`, `Resumed`, `Restarted`, `Submitted`                    | Assessment attempt lifecycle and timing                    |
| `AssessmentItemEvent` | `AssessmentProfile`       | `Started`, `Skipped`, `Completed`                                           | Question lifecycle and timing                              |
| `AssignableEvent`     | `AssignableProfile`       | `Activated`, `Deactivated`, `Started`, `Completed`, `Submitted`, `Reviewed` | Interaction with assigned content                          |
| `ViewEvent`           | Optional                  | `Viewed`                                                                    | Time spent viewing a resource                              |
| `GradeEvent`          | `GradingProfile`          | `Graded`                                                                    | XP, mastery, or a question result, selected by `scoreType` |
| `SessionEvent`        | Optional `SessionProfile` | `LoggedIn`, `LoggedOut`, `TimedOut`                                         | Session lifecycle                                          |

`AssessmentEvent` does not accept `Completed`; use `Submitted`. A completed question is an `AssessmentItemEvent`. These lifecycle events do not award XP just because they were accepted. Use the agreed scoring contract.

## AssessmentEvent

Set `object.type` to `Assessment` and provide its ID and nonempty `name`. `generated` is an `Attempt` with an ID URL, student `assignee`, resource URL in `assignable`, and `startedAtTime`. Include `endedAtTime` for `Submitted`. `count` is optional; when present it is a positive integer. Keep `assignable` aligned with `object.id`.

The optional `object.isPartOf` is a `DigitalResourceCollection`. Include an explicit course even when the student has one enrollment.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "id": "https://api.example.com/rostering/1.0/users/student-example",
    "type": "Person"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "extensions": {
    "subject": "Math",
    "course": {
      "id": "https://api.example.com/rostering/1.0/courses/course-example"
    }
  },
  "type": "AssessmentEvent",
  "profile": "AssessmentProfile",
  "action": "Submitted",
  "object": {
    "id": "https://learning.example.com/assessments/fractions",
    "type": "Assessment",
    "name": "Fractions Quiz"
  },
  "generated": {
    "id": "https://learning.example.com/attempts/attempt-example",
    "type": "Attempt",
    "assignee": {
      "id": "https://api.example.com/rostering/1.0/users/student-example",
      "type": "Person"
    },
    "assignable": "https://learning.example.com/assessments/fractions",
    "startedAtTime": "2026-09-11T14:55:00.000Z",
    "endedAtTime": "2026-09-11T15:00:00.000Z"
  }
}
```

## AssessmentItemEvent

Set `object.type` to `AssessmentItem` and include a nonempty `name`. `object.isPartOf` must identify the parent `Assessment`.

For `Completed`, `generated` must be a `Response` containing an `Attempt`. The attempt requires `startedAtTime` and `endedAtTime`. `generated` can be omitted for `Started` or `Skipped`. A response event does not encode correctness; send an agreed grade event for the score.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "id": "https://api.example.com/rostering/1.0/users/student-example",
    "type": "Person"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "extensions": {
    "subject": "Math",
    "course": {
      "id": "https://api.example.com/rostering/1.0/courses/course-example"
    }
  },
  "type": "AssessmentItemEvent",
  "profile": "AssessmentProfile",
  "action": "Completed",
  "object": {
    "id": "https://learning.example.com/questions/fraction-1",
    "type": "AssessmentItem",
    "name": "Add two fractions",
    "isPartOf": {
      "id": "https://learning.example.com/assessments/fractions",
      "type": "Assessment"
    }
  },
  "generated": {
    "id": "https://learning.example.com/responses/response-example",
    "type": "Response",
    "attempt": {
      "id": "https://learning.example.com/attempts/question-example",
      "type": "Attempt",
      "assignee": {
        "id": "https://api.example.com/rostering/1.0/users/student-example",
        "type": "Person"
      },
      "assignable": "https://learning.example.com/questions/fraction-1",
      "startedAtTime": "2026-09-11T14:55:00.000Z",
      "endedAtTime": "2026-09-11T15:00:00.000Z"
    }
  }
}
```

## AssignableEvent

Set `object.type` to `AssignableDigitalResource`. Include a nonempty `name` and `mediaType`. `generated` is an `Attempt` with `startedAtTime` for every action; `Completed` also requires `endedAtTime`. Pin `extensions.course.id` when a student can have multiple courses in the same app and subject.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "id": "https://api.example.com/rostering/1.0/users/student-example",
    "type": "Person"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "extensions": {
    "subject": "Math",
    "course": {
      "id": "https://api.example.com/rostering/1.0/courses/course-example"
    }
  },
  "type": "AssignableEvent",
  "profile": "AssignableProfile",
  "action": "Completed",
  "object": {
    "id": "https://learning.example.com/lessons/fractions",
    "type": "AssignableDigitalResource",
    "name": "Fractions lesson",
    "mediaType": "text/html"
  },
  "generated": {
    "id": "https://learning.example.com/attempts/attempt-example",
    "type": "Attempt",
    "assignee": {
      "id": "https://api.example.com/rostering/1.0/users/student-example",
      "type": "Person"
    },
    "assignable": "https://learning.example.com/lessons/fractions",
    "startedAtTime": "2026-09-11T14:55:00.000Z",
    "endedAtTime": "2026-09-11T15:00:00.000Z"
  }
}
```

## ViewEvent

`object.type` may be `DigitalResource`, `AssignableDigitalResource`, `Assessment`, or `AssessmentItem`. Include a nonempty `name`. `generated` is an `Attempt` with both start and end timestamps. `mediaType`, a parent `DigitalResourceCollection`, and `profile` are optional.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "id": "https://api.example.com/rostering/1.0/users/student-example",
    "type": "Person"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "extensions": {
    "subject": "Math",
    "course": {
      "id": "https://api.example.com/rostering/1.0/courses/course-example"
    }
  },
  "type": "ViewEvent",
  "action": "Viewed",
  "object": {
    "id": "https://learning.example.com/lessons/fractions",
    "type": "DigitalResource",
    "name": "Fractions explanation"
  },
  "generated": {
    "id": "https://learning.example.com/attempts/attempt-example",
    "type": "Attempt",
    "assignee": {
      "id": "https://api.example.com/rostering/1.0/users/student-example",
      "type": "Person"
    },
    "assignable": "https://learning.example.com/lessons/fractions",
    "startedAtTime": "2026-09-11T14:55:00.000Z",
    "endedAtTime": "2026-09-11T15:00:00.000Z"
  }
}
```

## GradeEvent

The actor is the grading `SoftwareApplication`. The student is `object.assignee`; `object` is the `Attempt`. Its `assignable` is a `DigitalResource` object with an ID and nonempty `mediaType`. Include `object.extensions.activityName` and an explicit course.

`generated` is a `Score` with an ID URL, `scoreGiven`, `maxScore`, and `extensions.scoreType`.

| `scoreType`       | Current metric interpretation                                                                      |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| `XP`              | `scoreGiven` is the XP value for the result; it is not divided by `maxScore`                       |
| `QUESTION_RESULT` | Counts one question; `scoreGiven >= 1` counts as correct                                           |
| `MASTERY`         | Records one mastered unit and the supplied score; the event does not calculate a passing threshold |

Only send `MASTERY` after your integration's mastery decision. A low score with this type is still a mastery assertion under the current mapping. Set `scoreType` to match the meaning of the event.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "extensions": {
    "subject": "Math",
    "course": {
      "id": "https://api.example.com/rostering/1.0/courses/course-example"
    }
  },
  "type": "GradeEvent",
  "profile": "GradingProfile",
  "action": "Graded",
  "object": {
    "id": "https://learning.example.com/attempts/attempt-example",
    "type": "Attempt",
    "assignee": {
      "id": "https://api.example.com/rostering/1.0/users/student-example",
      "type": "Person"
    },
    "assignable": {
      "id": "https://learning.example.com/lessons/fractions",
      "type": "DigitalResource",
      "mediaType": "text/html"
    },
    "extensions": {
      "activityName": "Fractions lesson"
    }
  },
  "generated": {
    "id": "https://learning.example.com/scores/score-example",
    "type": "Score",
    "scoreGiven": 10,
    "maxScore": 10,
    "extensions": {
      "scoreType": "XP"
    }
  }
}
```

## SessionEvent

| Action      | Actor                 | Object                | Required timing         |
| ----------- | --------------------- | --------------------- | ----------------------- |
| `LoggedIn`  | Student `Person`      | `SoftwareApplication` | `session.startedAtTime` |
| `LoggedOut` | Student `Person`      | `SoftwareApplication` | `session.endedAtTime`   |
| `TimedOut`  | `SoftwareApplication` | `Session`             | `object.endedAtTime`    |

Use a stable session URL and retain the same session identity through login, heartbeats, and logout. A timeout also needs a student identity that the receiver can resolve from the event or session context; validate that path with your integration before relying on it.

```json theme={null}
{
  "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
  "id": "urn:uuid:437620ad-f9c9-4a9d-9232-37676bfcf28e",
  "eventTime": "2026-09-11T15:00:00.000Z",
  "actor": {
    "id": "https://api.example.com/rostering/1.0/users/student-example",
    "type": "Person"
  },
  "edApp": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "type": "SessionEvent",
  "profile": "SessionProfile",
  "action": "LoggedIn",
  "object": {
    "@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
    "id": "https://api.example.com/applications/1.0/app-example",
    "type": "SoftwareApplication"
  },
  "session": {
    "id": "https://learning.example.com/sessions/session-example",
    "type": "Session",
    "startedAtTime": "2026-09-11T15:00:00.000Z"
  }
}
```

### Heartbeats

After a successful login event, send a heartbeat to `POST /events/1.0/sessions/{sessionId}/heartbeat` with `{"eventTime":"2026-09-11T15:01:00.000Z"}`. The API also accepts `POST /events/1.0/heartbeat` with `sessionId` and `eventTime` in the body, or `sessionId` in the query string. Use the session identifier expected by your integration.

A successful heartbeat returns `200`, `status`, `sessionId`, and `endedAtTime`. An unknown session returns `404`. A successful login response prepares the session before queuing further processing, so a database error during that preparation returns a retryable `503`.

## Processing boundaries

A grade or assessment event may validate and then be declined because its pinned course is missing or belongs to another application. A valid pinned course does not require an enrollment row to receive these events. Unpinned assignable and view events use enrollment context when available, which can be ambiguous when several courses match.

Payload validation checks structure. It does not prove that timing is plausible, a mastery assertion is deserved, or the intended result has appeared. Verify those outcomes through the result and analytics APIs available to your integration.
