App History
App history is the audit-style timeline for a single client app. It records lifecycle and approval activity such as discovery, approval status changes, and human-authored notes.
This is a child collection under an app because history is unbounded, time-ordered, and not always needed to render the primary App resource. See the Design Guidelines and App History Design Review.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| List History | GET | Paginated app history timeline, newest first |
| Add Note | POST | Append a note event to the app history timeline |
Approval events are not created by posting directly to /history. They are created as side effects of the app approval endpoints:
DTOs
ActorRef
Small embedded actor projection used inside app history events.
{
"id": "1b8fbc0f-f234-4da7-9cb2-5ae10ef63b8e",
"display_name": "Jane Smith"
}
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (uuid) | No | Stable unique identifier for the actor record |
display_name | string | No | Human-readable name to render in the timeline UI |
Notes:
- This is intentionally not UserRef. App history actors currently come from the
partner_usersdomain, not the clientusersdomain. - System-generated events use
actor: null. - If multiple actor domains are introduced later, this shape can be extended with
type.
AppHistoryEvent
Single timeline event for a client app.
{
"id": "9f4f8c52-3d85-4d0f-bec3-d95e890d1d24",
"event_type": "approval_status_changed",
"actor": {
"id": "1b8fbc0f-f234-4da7-9cb2-5ae10ef63b8e",
"display_name": "Jane Smith"
},
"message": "approved this app",
"comment": null,
"metadata": {
"is_approved": true
},
"created_at": "2026-03-06T19:42:11.000Z"
}
| Field | Type | Nullable | Description |
|---|---|---|---|
id | string (uuid) | No | Stable unique identifier for this history event |
event_type | string | No | Event discriminator: app_created, approval_status_changed, note_added |
actor | ActorRef | Yes | Actor who caused the event. null for system-generated events. |
message | string | Yes | Short timeline copy owned by the API for event display |
comment | string | Yes | Optional free-text note or annotation attached to the event. Typically populated on note_added events. |
metadata | object | Yes | Event-specific structured data |
created_at | string (ISO 8601) | No | When the history event was created |
Notes:
client_idandapp_idare omitted because they are already present in the URL path.- Events are append-only.
- Consumers must not infer business meaning from
messagetext alone; useevent_typeandmetadata. App.is_approvedandAppHistoryEvent.metadata.is_approvedare intentionally different concepts:App.is_approvedis the app's current approval stateAppHistoryEvent.metadata.is_approvedis the approval state produced by that specific history event
AppHistoryEvent.metadata
The metadata object is event-specific.
event_type | metadata shape |
|---|---|
app_created | null |
approval_status_changed | { "is_approved": boolean } |
note_added | null |
List History
GET /clients/{client_id}/apps/{app_id}/history
Returns a paginated timeline of app history events ordered by created_at descending (newest first).
When to use
Use this endpoint to:
- render the app detail timeline,
- show approval changes over time,
- display notes attached to the app,
- inspect discovery and audit-style activity for a single app.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
client_id | string (uuid) | Unique identifier for the client organization |
app_id | string (uuid) | Unique identifier for the app |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page_size | integer | 50 | Items per page. Each endpoint defines its own maximum. |
cursor | string | - | Opaque cursor from a previous response |
Response
| Field | Type | Description |
|---|---|---|
data | AppHistoryEvent[] | Array of history events, newest first |
total_count | integer | Total number of events for this app |
next_cursor | string | null | Cursor for the next page. null when on the last page. |
Example Request
curl -X GET "https://api.example.com/v2/clients/aa7cf840-9ca9-46a3-9778-9015d6580d50/apps/f1a2b3c4-d5e6-7890-abcd-ef1234567890/history" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Example Response
{
"data": [
{
"id": "9f4f8c52-3d85-4d0f-bec3-d95e890d1d24",
"event_type": "approval_status_changed",
"actor": {
"id": "1b8fbc0f-f234-4da7-9cb2-5ae10ef63b8e",
"display_name": "Jane Smith"
},
"message": "approved this app",
"comment": null,
"metadata": {
"is_approved": true
},
"created_at": "2026-03-06T19:42:11.000Z"
},
{
"id": "81ab9698-7837-43c1-8b89-6b3118b8b1f2",
"event_type": "note_added",
"actor": {
"id": "1b8fbc0f-f234-4da7-9cb2-5ae10ef63b8e",
"display_name": "Jane Smith"
},
"message": "added a note",
"comment": "Waiting for security sign-off",
"metadata": null,
"created_at": "2026-03-05T17:15:00.000Z"
},
{
"id": "d14a4cb9-b1e4-4fb9-b459-d4aaf7b0e1df",
"event_type": "app_created",
"actor": null,
"message": "App was discovered in your account",
"comment": null,
"metadata": null,
"created_at": "2026-02-18T09:00:00.000Z"
}
],
"total_count": 3,
"next_cursor": null
}
Pagination
This endpoint uses cursor-based pagination.
- Omit
cursoron the first request - If
next_cursoris notnull, pass its value ascursoron the next request - Repeat until
next_cursorisnull
Error Responses
| Status | Description |
|---|---|
| 400 | Invalid request (e.g. invalid UUID, malformed cursor) |
| 401 | Authentication required |
| 403 | Insufficient permissions for this client |
| 404 | App or client not found |
| 500 | Server error |
Example Error Response
{
"error": {
"code": "not_found",
"message": "App not found",
"details": {
"app_id": "f1a2b3c4-d5e6-7890-abcd-ef1234567890"
}
}
}
Notes
- History is returned newest first.
- Events are append-only.
- Approval endpoints append
approval_status_changedevents as side effects. - Approval events typically carry
metadata.is_approvedandcomment: null. metadata.is_approvedon a history event is not a duplicate ofApp.is_approved; it records the outcome of that event, while the app resource shows the current state.actoris a small embedded ref for rendering the timeline, not a full user DTO.- If the API is intended to serve as a strict audit trail,
actor.display_nameshould be snapshotted at write time rather than resolved dynamically.
Related Endpoints
- Get App - Full app detail
- Add App History Note - Append a note event to the timeline
- Approve Apps - Bulk approve apps and append approval history events
- Remove App Approvals - Bulk remove approvals and append approval history events