Skip to main content

API Overview

The AllMyChats API is built using tRPC (TypeScript Remote Procedure Call) on top of TanStack Start, providing end-to-end type safety between the client and server. This means you get full TypeScript autocomplete and type checking for all API calls.

Base URL​

/api/trpc

Key Features​

  • Type-Safe: Full TypeScript inference from server to client
  • Auto-completion: IDE autocomplete for all API procedures
  • Zero Runtime Overhead: No code generation required
  • Batching: Automatic request batching for better performance
  • Authentication: Better Auth integration for session-based auth
  • Rate Limiting: Built-in rate limiting for API endpoints

Tech Stack​

  • tRPC v11: Type-safe RPC framework
  • SuperJSON: Data transformer for dates, BigInt, and other special types
  • Zod: Schema validation for all inputs and outputs
  • Better Auth: Authentication and session management

Getting Started​

Client Setup​

The tRPC client is pre-configured in src/lib/trpc/client.ts:

import { trpc } from "~/lib/trpc/client";

// Use in React components
function MyComponent() {
const { data } = trpc.messages.list.useQuery({
limit: 50,
offset: 0
});

return <div>{data?.messages.length} messages</div>;
}

Vanilla Client (Server-Side)​

For server-side calls or outside React components:

import { trpcClient } from "~/lib/trpc/client";

const result = await trpcClient.messages.list.query({
limit: 50,
offset: 0,
});

Authentication​

All API endpoints (except those marked as public) require authentication. Authentication is handled via Better Auth session cookies.

Authentication Flow​

  1. User logs in via /api/auth/* endpoints
  2. Session is stored in an HTTP-only cookie
  3. tRPC automatically includes the cookie in requests
  4. Server validates session and extracts userId

Protected Procedures​

Procedures that require authentication use protectedProcedure:

import { protectedProcedure } from "~/server/trpc";

export const myRouter = router({
// Requires authentication
secretData: protectedProcedure.query(({ ctx }) => {
return { userId: ctx.userId };
}),
});

Context Type​

The tRPC context includes:

interface TRPCContext {
userId?: string; // User ID if authenticated
isAuthenticated: boolean; // Authentication status
headers: Headers; // Request headers
}

Rate Limiting​

The API implements rate limiting to prevent abuse and ensure fair usage. Rate limits are applied per IP address and/or per user.

Rate Limit Categories​

CategoryLimitWindowDescription
AUTH5 requests15 minutesLogin, register, password reset
API100 requests1 minuteGeneral API endpoints
UPLOAD10 requests1 hourFile upload operations
AI_CATEGORIZE50 requests1 minuteAI categorization
AI_SUMMARIZE50 requests1 minuteAI summarization
GLOBAL1000 requests1 minuteOverall application

Rate Limit Response​

When rate limited, the API returns:

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later.",
"retryAfter": 60,
"resetTime": "2025-01-18T12:00:00.000Z"
}
}

Response Headers​

All API responses include rate limit headers:

X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1737182400
Retry-After: 60

Error Handling​

tRPC provides structured error responses with proper TypeScript types.

Error Format​

{
error: {
code: "UNAUTHORIZED" | "FORBIDDEN" | "NOT_FOUND" | "INTERNAL_SERVER_ERROR" | "BAD_REQUEST",
message: "Human-readable error message",
data?: {
zodError?: ZodError // Validation errors (if applicable)
}
}
}

Error Codes​

CodeDescriptionHTTP Status
UNAUTHORIZEDAuthentication required401
FORBIDDENInsufficient permissions403
NOT_FOUNDResource not found404
BAD_REQUESTInvalid input400
INTERNAL_SERVER_ERRORServer error500
CONFLICTResource conflict409
PRECONDITION_FAILEDService unavailable412

Handling Errors​

const { data, error } = trpc.messages.getById.useQuery({ id: "123" });

if (error) {
switch (error.data?.code) {
case "NOT_FOUND":
console.log("Message not found");
break;
case "FORBIDDEN":
console.log("You don't have access");
break;
default:
console.log("Something went wrong");
}
}

Available API Procedures​

Messages (messages)​

Manage WhatsApp messages, including imports, updates, and categorization.

Queries​

ProcedureDescription
messages.listList messages with pagination and filtering
messages.getByIdGet a single message by ID
messages.searchSearch messages by content
messages.getCountGet total message count
messages.getCategoriesGet all categories for a message

Mutations​

ProcedureDescription
messages.createCreate a new message
messages.updateUpdate a message
messages.deleteDelete a message
messages.markAsReadMark message as read
messages.toggleStarredToggle starred status
messages.archiveArchive a message
messages.unarchiveUnarchive a message
messages.setCategoriesSet categories for a message (multi-category)
messages.addCategoryAdd a category to a message
messages.removeCategoryRemove a category from a message
messages.importWhatsAppImport messages from WhatsApp export

Categories (categories)​

Manage message categories and hierarchies.

Queries​

ProcedureDescription
categories.listList all categories
categories.listWithStatsList categories with message counts
categories.listRootList root categories (no parent)
categories.listChildrenList child categories of a parent
categories.getByIdGet a single category with stats
categories.getStatisticsGet comprehensive category statistics
categories.getCountGet category count

Mutations​

ProcedureDescription
categories.createCreate a new category
categories.updateUpdate a category
categories.deleteDelete a category
categories.updateSortOrderUpdate category sort order
categories.moveToParentMove category to new parent

Conversations (conversations)​

Manage chat conversations and messages for Q&A features.

Queries​

ProcedureDescription
conversations.listList conversations with pagination
conversations.getByIdGet a single conversation with stats
conversations.getCountGet conversation count
conversations.getMessagesGet messages for a conversation
conversations.getThreadMessagesGet thread messages (replies)

Mutations​

ProcedureDescription
conversations.createCreate a new conversation
conversations.updateUpdate a conversation
conversations.deleteDelete a conversation
conversations.archiveArchive a conversation
conversations.unarchiveUnarchive a conversation
conversations.sendMessageSend a message in a conversation
conversations.updateMessageUpdate a chat message
conversations.deleteMessageDelete a chat message

Comprehensive search functionality with tags and saved filters.

Queries​

ProcedureDescription
search.searchPerform a search (keyword, semantic, or hybrid)
search.parseQueryParse a search query for preview/validation
search.getSuggestionsGet search suggestions (recent/popular)
search.getTagsGet all tags for the current user
search.getMessageTagsGet tags for a message
search.getSavedFiltersGet all saved filters
search.getSavedFilterGet a saved filter by ID
search.getMostUsedFiltersGet most used saved filters
search.getRecentlyUsedFiltersGet recently used saved filters
search.getEmbeddingStatsGet embedding statistics

Mutations​

ProcedureDescription
search.clearHistoryClear search history
search.createTagCreate a new tag
search.updateTagUpdate a tag
search.deleteTagDelete a tag
search.addTagToMessageAdd tag to a message
search.removeTagFromMessageRemove tag from a message
search.setMessageTagsSet all tags for a message
search.bulkAddTagBulk add tag to multiple messages
search.bulkRemoveTagBulk remove tag from multiple messages
search.createSavedFilterCreate a saved filter
search.updateSavedFilterUpdate a saved filter
search.deleteSavedFilterDelete a saved filter
search.applySavedFilterApply a saved filter (increments usage)
search.reorderSavedFiltersReorder saved filters
search.generateEmbeddingsGenerate embeddings for messages

Categorization (categorization)​

AI-powered message categorization using LLM providers.

Queries​

ProcedureDescription
categorization.getStatsGet categorization statistics
categorization.getUncategorizedCountGet uncategorized message count
categorization.getMessagesNeedingReviewGet low-confidence messages
categorization.getCategoryOptionsGet category options for the user
categorization.getCategoriesWithStatsGet categories with message counts

Mutations​

ProcedureDescription
categorization.categorizeMessageCategorize a single message
categorization.categorizeBulkCategorize multiple messages
categorization.categorizeUncategorizedCategorize all uncategorized messages
categorization.suggestTaxonomySuggest a taxonomy based on messages
categorization.approveCategorizatonApprove a categorization (mark as reviewed)
categorization.suggestCategoryAssignmentsSuggest category assignments (preview)
categorization.applyCategoryAssignmentsApply category assignments after approval

Pipeline (pipeline)​

Message processing pipeline orchestration for import, categorize, and summarize jobs.

Queries​

ProcedureDescription
pipeline.getJobGet a pipeline job by ID
pipeline.listJobsList pipeline jobs with filtering
pipeline.getActiveJobsGet active pipeline jobs
pipeline.getRecentCompletedGet recent completed jobs
pipeline.getFailedJobsGet failed jobs
pipeline.getStatsGet pipeline job statistics

Mutations​

ProcedureDescription
pipeline.startImportStart an import pipeline job
pipeline.startCategorizeStart a categorization job
pipeline.startSummarizeStart a summarization job
pipeline.cancelJobCancel a running job
pipeline.deleteJobDelete a job
pipeline.cleanupClean up old completed jobs

Q&A (qa)​

Context-aware question and answer functionality using LLM with retrieved message context.

Queries​

ProcedureDescription
qa.getStatusGet Q&A service status and configuration
qa.previewContextPreview context that would be retrieved
qa.getConversationHistoryGet conversation history for Q&A context

Mutations​

ProcedureDescription
qa.askAsk a question with context-aware Q&A

Feature Votes (featureVotes)​

Feature request voting system.

Queries​

ProcedureDescription
featureVotes.listList all feature requests with vote status
featureVotes.getRemainingVotesGet user's remaining votes
featureVotes.getUserVotedIdsGet feature IDs the user has voted for

Mutations​

ProcedureDescription
featureVotes.voteCast a vote for a feature request
featureVotes.unvoteRemove a vote from a feature request

Feedback (feedback)​

User feedback submission and management.

Queries​

ProcedureDescription
feedback.listGet all feedback for the current user
feedback.getByIdGet feedback by ID
feedback.getCountGet feedback count for the current user

Mutations​

ProcedureDescription
feedback.submitSubmit feedback
feedback.updateStatusUpdate feedback status

Code Examples​

Basic Usage​

import { trpc } from "~/lib/trpc/client";

function MessageList() {
// List messages with filtering
const { data, isLoading, error } = trpc.messages.list.useQuery({
limit: 50,
categoryId: "category-123",
isStarred: true,
sortBy: "messageTimestamp",
sortOrder: "desc"
});

if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;

return (
<ul>
{data?.messages.map((msg) => (
<li key={msg.id}>{msg.content}</li>
))}
</ul>
);
}

Mutations​

import { trpc } from "~/lib/trpc/client";
import { useState } from "react";

function CreateMessage() {
const [content, setContent] = useState("");
const utils = trpc.useUtils();

const createMutation = trpc.messages.create.useMutation({
onSuccess: () => {
// Invalidate and refetch messages list
utils.messages.list.invalidate();
setContent("");
}
});

const handleSubmit = () => {
createMutation.mutate({
content,
senderName: "John Doe",
messageTimestamp: new Date()
});
};

return (
<div>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<button onClick={handleSubmit}>
{createMutation.isPending ? "Creating..." : "Create"}
</button>
</div>
);
}

Infinite Scroll​

import { trpc } from "~/lib/trpc/client";

function InfiniteMessageList() {
const [limit, setLimit] = useState(50);

const { data, fetchNextPage, hasNextPage, isFetching } =
trpc.messages.list.useInfiniteQuery(
{ limit },
{
getNextPageParam: (lastPage) => {
if (lastPage.messages.length < limit) return undefined;
return lastPage.messages.length;
}
}
);

return (
<div>
{data?.pages.map((page) => (
<div key={page.offset}>
{page.messages.map((msg) => (
<div key={msg.id}>{msg.content}</div>
))}
</div>
))}
{hasNextPage && (
<button
onClick={() => fetchNextPage()}
disabled={isFetching}
>
{isFetching ? "Loading..." : "Load More"}
</button>
)}
</div>
);
}

Server-Side Usage​

import { trpcClient } from "~/lib/trpc/client";
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/messages")({
loader: async () => {
// Fetch data server-side
const messages = await trpcClient.messages.list.query({
limit: 50,
offset: 0,
});

return { messages };
},
});

Webhooks​

The application supports webhooks for external services integration.

Stripe Webhook​

Endpoint: /api/stripe/webhook Method: POST

Handles Stripe payment events for subscription management.

Events Handled​

Event TypeDescription
checkout.session.completedNew subscription created
customer.subscription.createdSubscription created
customer.subscription.updatedSubscription updated
customer.subscription.deletedSubscription canceled

Webhook Signature​

The webhook verifies Stripe signatures for security:

const sig = request.headers.get("stripe-signature");
const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET);

Pagination​

Most list endpoints support pagination via limit and offset parameters:

interface PaginationParams {
limit: number; // Number of items per page (1-100)
offset: number; // Number of items to skip
}

Response Format​

interface PaginatedResponse<T> {
items: T[];
pagination: {
total: number; // Total number of items
limit: number; // Items per page
offset: number; // Current offset
hasMore: boolean; // Whether there are more items
};
}

Data Transformer (SuperJSON)​

The API uses SuperJSON as the data transformer, which enables:

  • Date serialization: Dates are properly serialized and deserialized
  • BigInt support: Large numbers are handled correctly
  • Map/Set support: Complex data structures work seamlessly
  • Circular references: Handles circular references in objects

This means you don't need to manually parse dates or handle special types:

// Date is automatically a Date object on the client
const { data } = await trpc.messages.list.query();
console.log(data.messages[0].messageTimestamp instanceof Date); // true

API Versioning​

The current API version is v1. Future versions will be introduced with breaking changes, and the old version will be maintained for a deprecation period.

Versioning Strategy​

  • tRPC: Type changes automatically enforce compatibility
  • Deprecation: Old procedures marked with @deprecated JSDoc comments
  • Breaking Changes: Major version increments
  • Backward Compatibility: Maintained when possible

Best Practices​

1. Type Inference​

Always use the auto-inferred types instead of manually typing:

// ❌ Don't do this
const data: MessageType = await trpc.messages.getById.query({ id: "123" });

// ✅ Do this - types are inferred automatically
const data = await trpc.messages.getById.query({ id: "123" });

2. Error Handling​

Always handle errors properly:

const { data, error } = trpc.messages.getById.useQuery({ id: "123" });

if (error) {
// Handle error based on error code
return <ErrorMessage error={error} />;
}

3. Optimistic Updates​

Use utils for optimistic updates:

const utils = trpc.useUtils();

const mutation = trpc.messages.update.useMutation({
onMutate: async (newData) => {
// Cancel outgoing refetches
await utils.messages.getById.cancel({ id: newData.id });

// Snapshot previous value
const previousData = utils.messages.getById.getData({ id: newData.id });

// Optimistically update
utils.messages.getById.setData({ id: newData.id }, newData);

// Return context with rollback function
return { previousData };
},
onError: (err, newData, context) => {
// Rollback on error
if (context?.previousData) {
utils.messages.getById.setData({ id: newData.id }, context.previousData);
}
},
onSuccess: () => {
// Refetch after success
utils.messages.getById.invalidate({ id: newData.id });
},
});

4. Batch Queries​

Use useQueries for parallel queries:

import { useQueries } from "@trpc/react-query";

const results = useQueries({
queries: [
{ trpc: trpc.messages.list, query: { limit: 50 } },
{ trpc: trpc.categories.list, query: undefined },
{ trpc: trpc.conversations.list, query: { limit: 20 } },
],
});

Debugging​

tRPC DevTools​

Enable tRPC devtools in development:

// In development, tRPC devtools are available
// in the browser's React DevTools

Logging​

All tRPC calls with duration > 1000ms are logged:

[tRPC] messages.list took 1234ms

Error Formatting​

Zod validation errors are formatted for easy debugging:

{
error: {
data: {
zodError: {
fieldErrors: {
content: ["Content is required"];
}
}
}
}
}

Next Steps​