Developer reference
API Reference
Every request (except health checks) requires a Bearer token. All responses are JSON.
On this page
Authentication
How to create, use, and manage API tokens.
All API endpoints (except GET /api/v1/health) require a valid Bearer token. Tokens are created through Settings → API Tokens in the web UI.
Creating a token
- Navigate to Settings → API Tokens in the web UI.
- Enter a token name and choose a scope (
readorread_write). - Click Create to generate the token.
Token values are shown only once at creation time. Be sure to copy and store them securely.
Token format
Tokens are 128 hex-character strings with a ms_ prefix:
<example>: ms_3c5a7f9b1e4d8c0a6f2e3b9d8c7a6f5e4d3c2b1a09876543210abcdef123456
Token scopes
| Scope | Description |
|---|---|
“read” |
Read-only access. Accepts GET endpoints only. |
“read_write” |
Allows write methods, subject to the person’s family role. Accepts POST, PUT, PATCH, and DELETE only when that role permits the action. |
Token scope and family role are independent permission checks. A read_write token permits write
requests only when the person’s current family role also permits that action. It never elevates a
viewer into a contributor, admin, or owner.
Including the token in requests
Send the token in the Authorization header as a Bearer token:
Authorization: Bearer ms_3c5a7f9b1e4d8c0a6f2e3b9d8c7a6f5e4d3c2b1a09876543210abcdef123456
Memories
Create, read, update, and delete memories in your family.
contributors and all_members memories in their
family. Viewers receive only all_members memories. Viewer tokens cannot create, update, or delete a
memory, even when the token has read_write scope. An inaccessible memory ID returns
404 Not Found through the API.
GET
/api/v1/memories
read scope
List memories allowed by the authenticated person’s family role, with cursor-based pagination. Viewer results
contain only memories whose audience is all_members; counts and searches use the same scope.
Parameters
| Param | Description |
|---|---|
cursor |
Pagination cursor from a previous next_cursor meta value. |
per_page |
Number of results per page (1–100, default 25). |
q |
Full-text search query across memory content. |
Response
{
"data": [
{ "id": "aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa",
"family_id": "bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb",
"created_by_user_id": "cccccccc-cccc-4ccc-cccc-cccccccccccc",
"title": "...", "transcript": "...",
"notes": "...", "recorded_at": "2025-01-01T00:00:00Z",
"location_name": "...",
"tags": [], "visibility": "visible", "audience": "all_members",
"transcription_status": "completed",
"transcription_completed_at": "2025-01-01T00:00:00Z",
"ai_data": null, "ai_extraction_status": null,
"ai_extraction_completed_at": null,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"first_memory": false,
"photos": [
{ "id": "dddddddd-dddd-4ddd-dddd-dddddddddddd",
"caption": "...", "taken_at": "2025-01-01T00:00:00Z",
"position": 0, "ai_description": null,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"filename": "photo.jpg", "content_type": "image/jpeg",
"byte_size": 123456,
"content_url": "/api/v1/memories/aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa/photos/dddddddd-dddd-4ddd-dddd-dddddddddddd/content" }
] }
],
"meta": {
"next_cursor": "cmVjb3JkZWQtYXQtdXVpZD18MQ==",
"total_count": 42
}
}
GET
/api/v1/memories/:id
read scope
Return a single memory’s details when the authenticated person’s role and the memory audience permit access.
A viewer receives 404 Not Found for a contributors memory.
Response
{
"data": {
"id": "aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa",
"family_id": "bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb",
"created_by_user_id": "cccccccc-cccc-4ccc-cccc-cccccccccccc",
"title": "...",
"transcript": "...",
"notes": "...",
"recorded_at": "2025-01-01T00:00:00Z",
"location_name": "...",
"tags": [],
"visibility": "visible",
"audience": "all_members",
"transcription_status": "completed",
"transcription_completed_at": "2025-01-01T00:00:00Z",
"ai_data": null,
"ai_extraction_status": null,
"ai_extraction_completed_at": null,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"first_memory": false,
"photos": [
{ "id": "dddddddd-dddd-4ddd-dddd-dddddddddddd",
"caption": "...", "taken_at": "2025-01-01T00:00:00Z",
"position": 0, "ai_description": null,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"filename": "photo.jpg", "content_type": "image/jpeg",
"byte_size": 123456,
"content_url": "/api/v1/memories/aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa/photos/dddddddd-dddd-4ddd-dddd-dddddddddddd/content" }
]
}
}
GET
/api/v1/memories/:memory_id/photos/:id/content
read scope
Redirects to a short-lived (5 minute), same-origin-initiated signed URL for the photo’s
image content, set to render inline. The content_url field on each photo above
points here — direct storage URLs are never returned in JSON. Only active photos on
memories owned by the authenticated token’s family are accessible; this endpoint does
not honor cross-family memory shares.
POST
/api/v1/memories
write scope + contributor role
Create a new memory. Returns 201 Created.
Accepted params
| Param | Description |
|---|---|
audio | Audio file blob (mutually exclusive with transcript). |
recorded_at | When the memory was recorded. |
duration_seconds | Audio duration in seconds. |
transcript_language | Language of the transcript. |
transcript | Text transcript (mutually exclusive with audio). |
title | Memory title. |
notes | Narrative notes. |
location_name | Location name. |
visibility | Visibility state. |
audience | contributors (owner, admin, and member) or all_members (also includes viewers). Defaults to contributors. |
pinned | Pin the memory. |
tags[] | Array of tags. |
Response (201)
{
"data": {
"id": 1,
"family_id": 1,
"created_by_user_id": 1,
...
}
}
PUT
/api/v1/memories/:id
write scope + permitted contributor
Update an existing memory. Returns 200 OK.
Accepted params
| Param | Description |
|---|---|
title | Memory title. |
notes | Narrative notes. |
recorded_at | When the memory was recorded. |
visibility | Visibility state. |
audience | contributors or all_members. Viewers cannot change this field. |
pinned | Pin the memory. |
tags[] | Array of tags. |
Response (200)
{
"data": {
"id": 1,
...
}
}
DELETE
/api/v1/memories/:id
write scope
Delete a memory. Audio attachments are purged.
Response
204 No Content
Tokens
Manage API tokens programmatically.
GET
/api/v1/tokens
read scope
List active (non-revoked) API tokens for the user's family.
Response
{
"data": [
{
"id": 1,
"name": "Mobile App",
"scope": "read_write",
"family_id": 1,
"last_used_at": "2025-01-01T00:00:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
]
}
POST
/api/v1/tokens
write scope
Create a new API token. Returns 201 with a one-time token field.
Accepted params
| Param | Description |
|---|---|
token[name] | Token name (max 100 chars). |
token[scope] | Scope: read or read_write. |
Response (201)
{
"data": {
"id": 1,
"name": "Mobile App",
"scope": "read_write",
"family_id": 1,
"last_used_at": null,
"created_at": "2025-01-01T00:00:00Z",
"token": "ms_3c5a7f9b1e4d8c0a6f2e3b9d8c7a6f5e4d3c2b1a09876543210abcdef123456"
}
}
token is shown only once.
DELETE
/api/v1/tokens/:id
write scope
Revoke an active API token.
Response
204 No Content
POST
/api/v1/tokens/:id/rotate
write scope
Rotate a token. Returns the new raw token once.
Response
{
"data": {
"id": 1,
"name": "Mobile App",
"scope": "read_write",
"family_id": 1,
"last_used_at": null,
"created_at": "2025-01-01T00:00:00Z",
"token": "ms_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
}
Family
Manage family members and send invitations.
Family Summary
GET
/api/v1/family
read scope
Returns summary information for the authenticated user's family.
Response
{
"data": {
"id": "ffffffff-ffff-4fff-ffff-ffffffffffff",
"name": "The Smiths",
"plan_code": "premium",
"member_count": 4
}
}
Disposition of Fields
Authenticated API account-management fields—such as family_id,
created_by_user_id, membership and user IDs, member email, plan and member counts, and
processing state—support family management and integration within the authenticated account.
These fields are intentionally not portable export fields.
Portable exports independently use family_name, minimal member name/role/joined_at, and archival
memory and media fields. They retain memory and media object IDs where needed to cross-reference archived
metadata and files, while omitting account-management IDs, email addresses, and processing state.
GET
/api/v1/family/members
read scope
List all members of the authenticated user's family. Every family role may read the family roster; only owners and admins may change it.
Response
{
"data": [
{
"id": "dddddddd-dddd-4ddd-dddd-dddddddddddd",
"user_id": "eeeeeeee-eeee-4eee-eeee-eeeeeeeeeeee",
"name": "Jane Doe",
"email": "jane@example.com",
"role": "owner",
"joined_at": "2025-01-01T00:00:00Z"
}
]
}
PATCH
/api/v1/family/members/:id
write scope + admin/owner
Update a family member's role. Requires admin or owner role.
Accepted params
| Param | Description |
|---|---|
role | New role: owner, admin, member, or viewer. |
Response (200)
{
"data": {
"id": "dddddddd-dddd-4ddd-dddd-dddddddddddd",
"user_id": "eeeeeeee-eeee-4eee-eeee-eeeeeeeeeeee",
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin",
"joined_at": "2025-01-01T00:00:00Z"
}
}
DELETE
/api/v1/family/members/:id
write scope + admin/owner
Remove a family member. Cannot remove yourself.
Response
204 No Content
POST
/api/v1/family/invitations
write scope + admin/owner
Send an invitation to join the family. An email is sent to the invitation address. Members and viewers cannot list, create, or revoke pending invitations.
Accepted params
| Param | Description |
|---|---|
invitation[email] | Email address of the person to invite. |
invitation[role] | Role to assign: owner, admin, member, or viewer. Only an owner may assign owner. |
Response (201)
{
"data": {
"id": 1,
"email": "invitee@example.com",
"role": "member",
"status": "pending"
}
}
Health
Check API availability without authentication.
Health
GET
/api/v1/health
No authentication required. Returns a JSON body confirming the API is operational.
Response
{
"status": "ok"
}
Error Responses
Standard error formats returned by all endpoints.
Error Responses
All API errors follow a consistent JSON structure with an error or errors object.
Single errors
Returned for authentication failures, forbidden access, and general errors.
{
"error": {
"code": "...",
"message": "..."
}
}
Validation errors
Returned when request body parameter validation fails. Fields are keyed by attribute.
{
"errors": {
"field": ["message"]
}
}