PikoClaw API Reference — all 11 endpoints with examples
API Reference¶
Complete documentation of all PikoClaw API endpoints. The API is a REST service deployed on Cloudflare Workers with SQLite (local) or D1 (production) storage.
Base URL¶
- Local Development —
http://localhost:8000 - Production (Cloudflare Workers) —
https://api.pappas.work
Authentication¶
PikoClaw uses no authentication in the current version. All endpoints are public. Production deployments should add:
// Example: Cloudflare Workers middleware
export async function withAuth(request: Request): Promise<boolean> {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token || !VALID_TOKENS.includes(token)) {
return false;
}
return true;
}
Global Response Format¶
All endpoints return JSON with optional error details:
interface ApiResponse<T> {
status: 'ok' | 'error';
data?: T;
error?: {
code: string; // e.g., 'NOT_FOUND', 'INVALID_QUERY'
message: string;
};
timestamp: string; // ISO 8601
}
Endpoints¶
1. Health Check¶
GET /health
Verify the API is running and responsive.
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"version": "0.5.0",
"uptime_ms": 123456,
"environment": "development"
},
"timestamp": "2026-03-27T14:32:15Z"
}
2. List Contacts¶
GET /contacts
Retrieve all contacts extracted from the email archive.
Query Parameters:
- limit (integer, optional, default=100) — Max results
- offset (integer, optional, default=0) — Pagination offset
- sort (string, optional, default=frequency) — Sort by: frequency, name, last_seen
- search (string, optional) — Filter by name/email substring
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"contacts": [
{
"id": "contact_001",
"name": "Alice Chen",
"email": "alice@example.com",
"message_count": 42,
"frequency": "high",
"last_seen": "2026-01-15T09:30:00Z",
"first_seen": "2025-01-01T08:00:00Z",
"sentiment": "positive",
"community_id": "team_a",
"authority_score": 0.82,
"hub_score": 0.45
},
{
"id": "contact_002",
"name": "Bob Martinez",
"email": "bob@example.com",
"message_count": 18,
"frequency": "medium",
"last_seen": "2026-02-20T14:15:00Z",
"first_seen": "2025-03-10T10:30:00Z",
"sentiment": "neutral",
"community_id": "team_b",
"authority_score": 0.51,
"hub_score": 0.72
}
],
"pagination": {
"total": 64,
"limit": 10,
"offset": 0,
"pages": 7
}
},
"timestamp": "2026-03-27T14:32:15Z"
}
3. Get Contact Details¶
GET /contacts/{contact_id}
Retrieve detailed information about a specific contact.
Path Parameters:
- contact_id (string, required) — Contact ID
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"id": "contact_001",
"name": "Alice Chen",
"email": "alice@example.com",
"message_count": 42,
"frequency": "high",
"last_seen": "2026-01-15T09:30:00Z",
"first_seen": "2025-01-01T08:00:00Z",
"sentiment": "positive",
"community_id": "team_a",
"authority_score": 0.82,
"hub_score": 0.45,
"message_ids": ["msg_001", "msg_004", "msg_012"],
"common_topics": [
{"topic_id": "project_planning", "frequency": 12},
{"topic_id": "budget_review", "frequency": 8}
]
},
"timestamp": "2026-03-27T14:32:15Z"
}
Error Response (404 Not Found):
{
"status": "error",
"error": {
"code": "NOT_FOUND",
"message": "Contact contact_001 not found"
},
"timestamp": "2026-03-27T14:32:15Z"
}
4. List Messages¶
GET /messages
Retrieve messages from the archive.
Query Parameters:
- limit (integer, optional, default=50) — Max results
- offset (integer, optional, default=0) — Pagination offset
- sort (string, optional, default=sent_at) — Sort by: sent_at, subject, from
- from (string, optional) — Filter by sender email
- thread_id (string, optional) — Filter by conversation thread
- topic_id (string, optional) — Filter by topic
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"messages": [
{
"id": "msg_001",
"thread_id": "thread_42",
"from_email": "alice@example.com",
"to_emails": ["bob@example.com", "charlie@example.com"],
"subject": "Q1 Planning Discussion",
"body": "Team, let's discuss the Q1 roadmap...",
"body_tokens": 184,
"sent_at": "2025-11-15T09:30:00Z",
"received_at": "2025-11-15T09:31:00Z",
"topic_id": "project_planning",
"search_score": 0.89
}
],
"pagination": {
"total": 247,
"limit": 20,
"offset": 0,
"pages": 13
}
},
"timestamp": "2026-03-27T14:32:15Z"
}
5. Get Message Details¶
GET /messages/{message_id}
Retrieve the full content of a specific message.
Path Parameters:
- message_id (string, required) — Message ID
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"id": "msg_001",
"thread_id": "thread_42",
"from_email": "alice@example.com",
"to_emails": ["bob@example.com", "charlie@example.com"],
"cc_emails": [],
"bcc_emails": [],
"subject": "Q1 Planning Discussion",
"body": "Team,\n\nLet's discuss the Q1 roadmap and timeline. Key items:\n1. Budget allocation\n2. Resource planning\n3. Milestone definitions\n\nPlease review and comment by Friday.\n\nBest,\nAlice",
"body_tokens": 184,
"sent_at": "2025-11-15T09:30:00Z",
"received_at": "2025-11-15T09:31:00Z",
"topic_id": "project_planning",
"thread_messages": ["msg_001", "msg_004", "msg_012"],
"attachments": []
},
"timestamp": "2026-03-27T14:32:15Z"
}
6. Search Messages¶
POST /search
Full-text search across all messages using TF-IDF ranking.
Request Body:
interface SearchRequest {
query: string; // Search terms
limit?: number; // Max results (default: 20)
filters?: {
from?: string; // Sender email filter
topic_id?: string; // Topic filter
date_from?: string; // ISO 8601 date
date_to?: string; // ISO 8601 date
};
}
Request:
curl -X POST http://localhost:8000/search \
-H 'Content-Type: application/json' \
-d '{
"query": "budget planning timeline",
"limit": 10,
"filters": {
"topic_id": "project_planning",
"date_from": "2025-11-01"
}
}'
Response (200 OK):
{
"status": "ok",
"data": {
"query": "budget planning timeline",
"results": [
{
"message_id": "msg_001",
"rank": 1,
"score": 0.94,
"snippet": "...Q1 roadmap and timeline. Key items: Budget allocation, Resource planning...",
"from_email": "alice@example.com",
"subject": "Q1 Planning Discussion",
"sent_at": "2025-11-15T09:30:00Z"
},
{
"message_id": "msg_045",
"rank": 2,
"score": 0.87,
"snippet": "...timeline for budget review is December 15th. Please prepare...",
"from_email": "bob@example.com",
"subject": "Budget Review Timeline",
"sent_at": "2025-12-01T14:00:00Z"
}
],
"query_time_ms": 45
},
"timestamp": "2026-03-27T14:32:15Z"
}
7. List Topics¶
GET /topics
Retrieve all discovered topics/clusters from the archive.
Query Parameters:
- limit (integer, optional, default=50) — Max results
- sort (string, optional, default=frequency) — Sort by: frequency, name
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"topics": [
{
"id": "project_planning",
"name": "Project Planning",
"description": "Q1 roadmap, timeline, milestones, deliverables",
"message_count": 31,
"top_keywords": ["planning", "timeline", "milestones", "deliverables", "roadmap"],
"created_at": "2025-11-01T00:00:00Z"
},
{
"id": "budget_review",
"name": "Budget Review",
"description": "Q1 budget allocation, resource planning, approvals",
"message_count": 18,
"top_keywords": ["budget", "allocation", "resources", "approval", "spending"],
"created_at": "2025-11-01T00:00:00Z"
}
],
"total": 8
},
"timestamp": "2026-03-27T14:32:15Z"
}
8. Get Topic Details¶
GET /topics/{topic_id}
Retrieve detailed information about a specific topic.
Path Parameters:
- topic_id (string, required) — Topic ID
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"id": "project_planning",
"name": "Project Planning",
"description": "Q1 roadmap, timeline, milestones, deliverables",
"message_count": 31,
"top_keywords": ["planning", "timeline", "milestones", "deliverables", "roadmap"],
"key_contacts": [
{"contact_id": "contact_001", "name": "Alice Chen", "frequency": 12},
{"contact_id": "contact_003", "name": "Diana Wu", "frequency": 8}
],
"message_ids": ["msg_001", "msg_004", "msg_012"],
"date_range": {
"earliest": "2025-11-01T08:00:00Z",
"latest": "2026-01-15T16:30:00Z"
},
"created_at": "2025-11-01T00:00:00Z"
},
"timestamp": "2026-03-27T14:32:15Z"
}
9. List Threads¶
GET /threads
Retrieve email conversation threads.
Query Parameters:
- limit (integer, optional, default=50) — Max results
- offset (integer, optional, default=0) — Pagination offset
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"threads": [
{
"id": "thread_42",
"subject": "Q1 Planning Discussion",
"message_count": 7,
"participants": ["alice@example.com", "bob@example.com", "charlie@example.com"],
"date_range": {
"first_message": "2025-11-15T09:30:00Z",
"last_message": "2025-11-20T14:00:00Z"
},
"topic_id": "project_planning"
}
],
"pagination": {
"total": 42,
"limit": 20,
"offset": 0,
"pages": 3
}
},
"timestamp": "2026-03-27T14:32:15Z"
}
10. Get Thread Messages¶
GET /threads/{thread_id}/messages
Retrieve all messages in a conversation thread.
Path Parameters:
- thread_id (string, required) — Thread ID
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"thread_id": "thread_42",
"subject": "Q1 Planning Discussion",
"messages": [
{
"id": "msg_001",
"from_email": "alice@example.com",
"to_emails": ["bob@example.com", "charlie@example.com"],
"subject": "Q1 Planning Discussion",
"body": "Team, let's discuss the Q1 roadmap...",
"sent_at": "2025-11-15T09:30:00Z"
},
{
"id": "msg_004",
"from_email": "bob@example.com",
"to_emails": ["alice@example.com"],
"subject": "RE: Q1 Planning Discussion",
"body": "Sounds good. I have concerns about resource allocation...",
"sent_at": "2025-11-16T10:15:00Z"
}
]
},
"timestamp": "2026-03-27T14:32:15Z"
}
11. Statistics¶
GET /stats
Retrieve high-level statistics about the extracted archive.
Request:
Response (200 OK):
{
"status": "ok",
"data": {
"total_messages": 247,
"total_contacts": 64,
"total_topics": 8,
"total_threads": 42,
"pii_redactions": 512,
"date_range": {
"earliest": "2025-01-01T08:00:00Z",
"latest": "2026-03-27T16:30:00Z"
},
"extraction_timestamp": "2026-03-27T14:32:18Z",
"pipeline_version": "0.5.0"
},
"timestamp": "2026-03-27T14:32:15Z"
}
Error Codes¶
| Code | Status | Description |
|---|---|---|
OK |
200 | Success |
INVALID_QUERY |
400 | Invalid request parameters |
NOT_FOUND |
404 | Resource not found |
INTERNAL_ERROR |
500 | Server error |
UNIMPLEMENTED |
501 | Endpoint not yet implemented |
Rate Limiting¶
No rate limiting in development. Production deployments should add:
// Cloudflare Workers KV-based rate limiter
const RATE_LIMIT = 100; // requests per minute
const WINDOW_MS = 60000; // 1 minute
TypeScript Types¶
For client library integration:
interface Contact {
id: string;
name: string;
email: string;
message_count: number;
frequency: 'high' | 'medium' | 'low';
last_seen: string; // ISO 8601
first_seen: string; // ISO 8601
sentiment: 'positive' | 'neutral' | 'negative';
community_id: string;
authority_score: number; // HITS algorithm
hub_score: number; // HITS algorithm
}
interface Message {
id: string;
thread_id: string;
from_email: string;
to_emails: string[];
subject: string;
body: string;
body_tokens: number;
sent_at: string; // ISO 8601
received_at: string; // ISO 8601
topic_id: string;
search_score?: number;
}
interface Topic {
id: string;
name: string;
description?: string;
message_count: number;
top_keywords: string[];
created_at: string; // ISO 8601
}
interface SearchResult {
message_id: string;
rank: number;
score: number;
snippet: string;
from_email: string;
subject: string;
sent_at: string;
}
Example: Complete Workflow¶
# 1. Check API health
curl http://localhost:8000/health
# 2. Get statistics
curl http://localhost:8000/stats
# 3. Search for budget-related messages
curl -X POST http://localhost:8000/search \
-H 'Content-Type: application/json' \
-d '{"query": "budget allocation Q1"}'
# 4. Examine a specific topic
curl http://localhost:8000/topics/project_planning
# 5. Get key contacts in that topic
curl 'http://localhost:8000/contacts?limit=5&sort=frequency'
Next Steps¶
- Quickstart — Get up and running locally
- Deployment — Deploy to Cloudflare
- Pipeline — Understand the extraction process
Version: 0.5.0 | Last Updated: Mar 27, 2026