Results API

Results API

Endpoints

  • POST /results/search: Search and retrieve results with pagination
  • DELETE /results/:id: Soft-delete a result by id (marks as DELETED and removes payload)
  • POST /results/:id/feedback: Add feedback to a result

Authentication

All endpoints are protected by JWT. Pass a valid bearer token.

Search

Request body:

interface SearchResultsRequest {
  userEmails: string[];        // Required
  type?: 'thread';
  dateFrom?: Date;
  dateTo?: Date;
  page?: number;               // default: 1
  pageSize?: number;           // default: 10
}

Response:

interface SearchResultsResponse {
  results: OmnilexResult[];    // Can include items with status === 'DELETED'
  pagination: {
    currentPage: number;
    pageSize: number;
    totalPages: number;
    totalCount: number;
    hasNextPage: boolean;
    hasPreviousPage: boolean;
  };
}

Example:

curl -X POST \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "userEmails": ["user@example.com"],
    "type": "table",
    "page": 1,
    "pageSize": 10
  }' \
  http://localhost:3000/results-history/search

Soft-delete

Deletes do not remove the DB row. The item is marked as DELETED and its payload is removed.

  • Endpoint: DELETE /results-history/:id
  • Authorization: The requester must be the ownerUsername or be listed in can_write.
  • Effect:
    • status becomes DELETED
    • Payload field is removed based on type:
      • thread → the thread field is removed
    • Other metadata (id, owner, permissions, date, type) are preserved

Responses:

// 200 OK
{ "success": true }
 
// 404 Not Found
{ "error": "Result not found" }
 
// 403 Forbidden
{ "error": "You do not have permission to delete this result" }

Example:

curl -X DELETE \
  -H "Authorization: Bearer <TOKEN>" \
  http://localhost:3000/results-history/00000000-0000-0000-0000-000000000000

Feedback

Adds user feedback to a specific result.

  • Endpoint: POST /results/:id/feedback
  • Authorization: Requires valid JWT token

Request body:

interface AddFeedbackRequest {
  vote: 'upvote' | 'downvote' | 'undefined';  // FeedbackVote enum
  feedback: string;                            // User's feedback text
}

Response:

interface AddFeedbackResponse {
  success: boolean;
  message?: string;
}

Responses:

// 200 OK
{
  "success": true,
  "message": "Feedback added successfully"
}
 
// 403 Forbidden (if no permission)
{
  "success": false,
  "message": "You do not have permission to add feedback to this result"
}
 
// 404 Not Found
{
  "success": false,
  "message": "Result not found"
}
 
// 500 Internal Server Error
{
  "success": false,
  "message": "Failed to add feedback"
}

Example:

curl -X POST \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "vote": "upvote",
    "feedback": "This was very helpful!"
  }' \
  http://localhost:3000/results/00000000-0000-0000-0000-000000000000/feedback

Frontend usage (search)

async function fetchResults(page = 1, pageSize = 20) {
  const response = await fetch('/api/results-history/search', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ userEmails: ['user@example.com'], page, pageSize })
  });
  return response.json();
}

Frontend usage (feedback)

async function addFeedback(resultId: string, vote: 'upvote' | 'downvote' | 'undefined', feedback: string) {
  const response = await fetch(`/api/results/${resultId}/feedback`, {
    method: 'POST',
    headers: { 
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${getToken()}`
    },
    body: JSON.stringify({ vote, feedback })
  });
  return response.json();
}

Notes

  • Results are ordered by updatedAt timestamp
  • Pagination uses offset-based implementation internally
  • Responses include pagination metadata
  • Default page size is 10