@brandcast_app/cozi-api-client
v0.1.0
Published
Unofficial TypeScript/JavaScript client for Cozi Family Organizer API (reverse-engineered)
Maintainers
Readme
Cozi API Client
Unofficial TypeScript/JavaScript client for the Cozi Family Organizer API.
⚠️ Important Disclaimer
This is an UNOFFICIAL client library. Cozi does not provide a public API, and this library is based on reverse engineering from the py-cozi Python library.
Use at your own risk:
- The API may change without notice
- Your account could be suspended for using unofficial clients
- This library is provided AS-IS with no warranties
- Not affiliated with or endorsed by Cozi
Features
- 🔐 Username/password authentication
- 📝 Full CRUD operations for lists (shopping & todo)
- ✅ Item management (add, edit, mark, remove)
- 📦 TypeScript support with full type definitions
- 🛡️ Error handling and type safety
- 🐛 Optional debug logging
Installation
npm install @brandcast_app/cozi-api-clientor
yarn add @brandcast_app/cozi-api-clientQuick Start
import { CoziApiClient } from '@brandcast_app/cozi-api-client';
// Create a client instance
const client = new CoziApiClient({
debug: true, // Optional: enable debug logging
timeout: 30000, // Optional: request timeout in ms (default: 30000)
userAgent: 'my-app', // Optional: custom user agent
});
// Authenticate
const auth = await client.authenticate('[email protected]', 'your-password');
console.log('Authenticated with account:', auth.accountId);
// Get all lists
const lists = await client.getLists();
console.log('Found lists:', lists.length);
// Work with lists
for (const list of lists) {
console.log(`${list.title} (${list.listType}): ${list.items.length} items`);
for (const item of list.items) {
console.log(` - [${item.status}] ${item.text}`);
}
}API Reference
Authentication
authenticate(username: string, password: string): Promise<CoziAuthResponse>
Authenticate with Cozi using your email and password.
const auth = await client.authenticate('[email protected]', 'your-password');
// Returns: { accountId, accountPersonId, accessToken, expiresIn }setSessionToken(token: string, accountId?: string): void
Restore a previous session using a stored access token.
client.setSessionToken(storedToken, storedAccountId);Lists
getLists(): Promise<CoziList[]>
Get all lists for the authenticated user.
const lists = await client.getLists();getList(listId: string): Promise<CoziList>
Get a specific list by ID.
const list = await client.getList('list-id');addList(request: AddListRequest): Promise<string>
Create a new list.
const listId = await client.addList({
title: 'Grocery Shopping',
type: 'shopping' // 'shopping' or 'todo'
});removeList(listId: string): Promise<void>
Delete a list.
await client.removeList('list-id');reorderList(request: ReorderListRequest): Promise<void>
Change the order of a list.
await client.reorderList({
listId: 'list-id',
newOrder: 2
});Items
addItem(request: AddItemRequest): Promise<void>
Add an item to a list.
await client.addItem({
listId: 'shopping-list-id',
text: 'Milk'
});editItem(request: EditItemRequest): Promise<void>
Edit an existing item.
await client.editItem({
listId: 'shopping-list-id',
itemId: 'item-id',
text: 'Whole Milk'
});markItem(request: MarkItemRequest): Promise<void>
Mark an item as complete or incomplete.
// Mark as complete
await client.markItem({
listId: 'shopping-list-id',
itemId: 'item-id',
completed: true
});
// Mark as incomplete
await client.markItem({
listId: 'shopping-list-id',
itemId: 'item-id',
completed: false
});removeItem(request: RemoveItemRequest): Promise<void>
Remove an item from a list.
await client.removeItem({
listId: 'shopping-list-id',
itemId: 'item-id'
});Type Definitions
CoziList
interface CoziList {
listId: string;
title: string;
listType: 'shopping' | 'todo';
items: CoziItem[];
version: number;
notes?: string | null;
owner?: string | null;
}CoziItem
interface CoziItem {
itemId: string;
text: string;
status: 'incomplete' | 'complete';
itemType?: string | null;
dueDate?: string | null;
notes?: string | null;
owner?: string | null;
version: number;
}CoziAuthResponse
interface CoziAuthResponse {
accountId: string;
accountPersonId: string;
accessToken: string;
expiresIn: number; // seconds
}Complete Example
import { CoziApiClient } from '@brandcast_app/cozi-api-client';
async function main() {
// Create client with debug logging
const client = new CoziApiClient({ debug: true });
try {
// Authenticate
const auth = await client.authenticate('[email protected]', 'your-password');
console.log('✓ Authenticated successfully');
// Get all lists
const lists = await client.getLists();
console.log(`✓ Found ${lists.length} lists`);
// Find shopping list
const shoppingList = lists.find(l => l.listType === 'shopping');
if (!shoppingList) {
throw new Error('No shopping list found');
}
// Add an item
await client.addItem({
listId: shoppingList.listId,
text: 'Organic Milk'
});
console.log('✓ Added item to shopping list');
// Get updated list
const updatedList = await client.getList(shoppingList.listId);
const newItem = updatedList.items.find(i => i.text === 'Organic Milk');
if (newItem) {
// Mark it as complete
await client.markItem({
listId: shoppingList.listId,
itemId: newItem.itemId,
completed: true
});
console.log('✓ Marked item as complete');
// Remove it
await client.removeItem({
listId: shoppingList.listId,
itemId: newItem.itemId
});
console.log('✓ Removed item from list');
}
} catch (error) {
console.error('Error:', error);
}
}
main();Error Handling
The client throws errors that conform to the CoziApiError interface:
interface CoziApiError {
code: string;
message: string;
details?: unknown;
}Example error handling:
try {
await client.addItem({
listId: 'invalid-list-id',
text: 'Test item'
});
} catch (error) {
const coziError = error as CoziApiError;
console.error('Error code:', coziError.code);
console.error('Error message:', coziError.message);
console.error('Error details:', coziError.details);
}Session Management
Access tokens expire after a certain period (specified in expiresIn from the auth response). You can store the token and accountId to avoid re-authenticating:
// Initial authentication
const auth = await client.authenticate('[email protected]', 'your-password');
// Store token securely
localStorage.setItem('cozi_token', auth.accessToken);
localStorage.setItem('cozi_account_id', auth.accountId);
// Later, restore session
const storedToken = localStorage.getItem('cozi_token');
const storedAccountId = localStorage.getItem('cozi_account_id');
if (storedToken && storedAccountId) {
client.setSessionToken(storedToken, storedAccountId);
// Now you can make API calls without re-authenticating
}Development
Building
npm install
npm run buildTesting
npm testCredits
This library is based on the excellent reverse engineering work done in the py-cozi Python library.
Legal
This is an unofficial library and is not affiliated with, endorsed by, or connected to Cozi. Use at your own risk. The developers of this library are not responsible for any issues that may arise from its use.
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
Changelog
0.1.0 (Initial Release)
- ✨ Initial implementation
- 🔐 Username/password authentication
- 📝 List management (create, read, update, delete, reorder)
- ✅ Item management (add, edit, mark, remove)
- 📦 Full TypeScript support
- 🐛 Debug logging option
