Client API Endpoints
The Client API provides authenticated endpoints for registered users to manage their submitted items, view dashboard statistics, and access geographic data. All endpoints require session-based authentication via requireClientAuth().
Source directory: template/app/api/client/
Authentication
Every endpoint in this group requires a valid user session. Unauthenticated requests receive:
Status 401
{
"success": false,
"error": "Unauthorized. Please sign in to continue."
}
Dashboard Statistics
Get Dashboard Stats
Returns comprehensive dashboard statistics for the authenticated user.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/dashboard/stats |
| Auth | Session (user) |
| Source | client/dashboard/stats/route.ts |
Response
Status 200
{
"success": true,
"totalSubmissions": 23,
"totalViews": 0,
"totalVotesReceived": 156,
"totalCommentsReceived": 89,
"viewsAvailable": false,
"recentActivity": {
"newSubmissions": 3,
"newViews": 0
},
"uniqueItemsInteracted": 45,
"totalActivity": 237,
"activityChartData": [
{ "date": "Mon", "submissions": 1, "views": 0, "engagement": 5 }
],
"engagementChartData": [
{ "name": "Votes", "value": 156, "color": "#8884d8" }
],
"submissionTimeline": [
{ "month": "Mar", "submissions": 5 }
],
"engagementOverview": [
{ "week": "W1", "votes": 10, "comments": 3 }
],
"statusBreakdown": [
{ "status": "Approved", "value": 15, "color": "#22c55e" },
{ "status": "Pending", "value": 5, "color": "#f59e0b" },
{ "status": "Rejected", "value": 3, "color": "#ef4444" }
],
"topItems": [
{ "id": "item_1", "title": "My Tool", "views": 100, "votes": 25, "comments": 8 }
]
}
curl Example
curl -s http://localhost:3000/api/client/dashboard/stats \
-H "Cookie: next-auth.session-token=<session_token>"
Get Geographic Stats
Returns geographic coverage statistics for the authenticated user's items.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/geo-stats |
| Auth | Session (user) |
| Source | client/geo-stats/route.ts |
Response
Status 200
{
"success": true,
"total_items": 10,
"items_with_location": 7,
"items_remote": 2,
"service_area_breakdown": [
{ "area": "local", "count": 3 },
{ "area": "regional", "count": 2 }
],
"top_cities": [
{ "city": "New York", "count": 3 }
],
"top_countries": [
{ "country": "United States", "count": 5 }
]
}
curl Example
curl -s http://localhost:3000/api/client/geo-stats \
-H "Cookie: next-auth.session-token=<session_token>"
Get Item Coordinates
Returns coordinates for all user items that have location data, suitable for map rendering.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/items/coordinates |
| Auth | Session (user) |
| Source | client/items/coordinates/route.ts |
Response
Status 200
{
"success": true,
"coordinates": [
{
"slug": "my-item",
"name": "My Item",
"latitude": 40.7128,
"longitude": -74.006
}
]
}
curl Example
curl -s http://localhost:3000/api/client/items/coordinates \
-H "Cookie: next-auth.session-token=<session_token>"
Items Management
List User Items
Returns a paginated list of items submitted by the authenticated user.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/items |
| Auth | Session (user) |
| Source | client/items/route.ts |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page | integer | No | 1 | Page number (min: 1) |
limit | integer | No | 10 | Items per page (1-100) |
status | string | No | -- | Filter: all, pending, approved, rejected |
search | string | No | -- | Search by item name or description |
sortBy | string | No | -- | Sort field |
sortOrder | string | No | -- | Sort direction |
deleted | boolean | No | false | If true, returns soft-deleted items |
Response
Status 200
{
"success": true,
"items": [ /* item objects */ ],
"total": 23,
"page": 1,
"limit": 10,
"totalPages": 3,
"stats": {
"total": 20,
"pending": 3,
"approved": 15,
"rejected": 2,
"deleted": 1
}
}
curl Example
# List approved items, page 2
curl -s "http://localhost:3000/api/client/items?status=approved&page=2&limit=10" \
-H "Cookie: next-auth.session-token=<session_token>"
# Search for items
curl -s "http://localhost:3000/api/client/items?search=productivity" \
-H "Cookie: next-auth.session-token=<session_token>"
# List deleted items
curl -s "http://localhost:3000/api/client/items?deleted=true" \
-H "Cookie: next-auth.session-token=<session_token>"
Create Item
Creates a new item submission. The item is set to pending status for admin review.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/client/items |
| Auth | Session (user) |
| Source | client/items/route.ts |
Request Body
{
"name": "Awesome Tool",
"description": "A great productivity tool that helps teams collaborate.",
"source_url": "https://example.com",
"category": "Productivity",
"tags": ["collaboration", "remote-work"],
"icon_url": "https://example.com/icon.png"
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Item name (3-100 characters) |
description | string | Yes | Item description (10-500 characters) |
source_url | string (URI) | Yes | Main URL/link for the item |
category | string | string[] | No | Category name or array of categories |
tags | string[] | No | Array of tag strings |
icon_url | string (URI) | No | URL to item icon |
Response
Status 201
{
"success": true,
"item": { /* created item object */ },
"message": "Item submitted successfully. It will be reviewed by our team before being published."
}
Status 400 -- Validation error
{
"success": false,
"error": "Name must be at least 3 characters"
}
curl Example
curl -s -X POST http://localhost:3000/api/client/items \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=<session_token>" \
-d '{
"name": "Awesome Tool",
"description": "A great productivity tool that helps teams collaborate effectively.",
"source_url": "https://example.com",
"category": "Productivity",
"tags": ["collaboration"]
}'
Get Single Item
Returns details of a specific item owned by the authenticated user.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/items/{id} |
| Auth | Session (user, owner) |
| Source | client/items/[id]/route.ts |
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Item ID |
Response
Status 200
{
"success": true,
"item": { /* item object */ },
"engagement": {
"views": 150,
"likes": 23
}
}
| Status | Description |
|---|---|
| 400 | Invalid item ID |
| 401 | Unauthorized |
| 403 | Not the item owner |
| 404 | Item not found |
Update Item
Updates an item owned by the authenticated user. If the item was previously approved, updating it changes its status to pending for re-review.
| Property | Value |
|---|---|
| Method | PUT |
| Path | /api/client/items/{id} |
| Auth | Session (user, owner) |
| Source | client/items/[id]/route.ts |
Request Body
All fields are optional. At least one field must be provided.
{
"name": "Updated Tool Name",
"description": "Updated description with more details.",
"source_url": "https://example.com/v2",
"category": ["Productivity", "Developer Tools"],
"tags": ["collaboration", "ai"],
"icon_url": "https://example.com/new-icon.png"
}
Response
Status 200
{
"success": true,
"item": { /* updated item object */ },
"statusChanged": true,
"previousStatus": "approved",
"message": "Item updated successfully. Since it was previously approved, it has been moved to pending for re-review."
}
| Field | Type | Description |
|---|---|---|
statusChanged | boolean | true if status changed from approved to pending |
previousStatus | string | The item's status before the update |
curl Example
curl -s -X PUT http://localhost:3000/api/client/items/item_123 \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=<session_token>" \
-d '{ "name": "Updated Tool Name" }'
Delete Item (Soft Delete)
Soft-deletes an item owned by the authenticated user. The item is hidden but can be restored later.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/client/items/{id} |
| Auth | Session (user, owner) |
| Source | client/items/[id]/route.ts |
Response
Status 200
{
"success": true,
"message": "Item deleted successfully"
}
| Status | Description |
|---|---|
| 400 | Item is already deleted |
| 401 | Unauthorized |
| 403 | Not the item owner |
| 404 | Item not found |
Restore Item
Restores a previously soft-deleted item.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/client/items/{id}/restore |
| Auth | Session (user, owner) |
| Source | client/items/[id]/restore/route.ts |
Response
Status 200
{
"success": true,
"item": { /* restored item object */ },
"message": "Item restored successfully"
}
| Status | Description |
|---|---|
| 400 | Item is not deleted (cannot restore an active item) |
| 401 | Unauthorized |
| 403 | Not the item owner |
| 404 | Item not found |
curl Example
curl -s -X POST http://localhost:3000/api/client/items/item_123/restore \
-H "Cookie: next-auth.session-token=<session_token>"
Get Submission Statistics
Returns statistics about the authenticated user's submissions grouped by status.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/client/items/stats |
| Auth | Session (user) |
| Source | client/items/stats/route.ts |
Response
Status 200
{
"success": true,
"stats": {
"total": 12,
"draft": 2,
"pending": 3,
"approved": 5,
"rejected": 2,
"deleted": 1
}
}
curl Example
curl -s http://localhost:3000/api/client/items/stats \
-H "Cookie: next-auth.session-token=<session_token>"
TypeScript Usage
import type { ClientCreateItemResponse } from '@/lib/types/client-item';
// Fetch dashboard stats
const dashboardRes = await fetch('/api/client/dashboard/stats');
const dashboard = await dashboardRes.json();
// Create a new item submission
const createRes = await fetch('/api/client/items', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'My New Tool',
description: 'A detailed description of what this tool does.',
source_url: 'https://mytool.com',
category: 'Productivity',
}),
});
const created: ClientCreateItemResponse = await createRes.json();
// Update an item
const updateRes = await fetch(`/api/client/items/${itemId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Updated Name' }),
});
const updated = await updateRes.json();
if (updated.statusChanged) {
console.log('Item moved back to pending for re-review');
}
Error Response Pattern
All client API endpoints follow a consistent error shape:
{
"success": false,
"error": "Human-readable error message"
}
Error responses use the serverErrorResponse() utility, which logs detailed error information server-side while returning only a generic message to the client to prevent information disclosure.