|
| 1 | +# Reputation API — integration guide |
| 2 | + |
| 3 | +Backend reputation metrics are **pre-aggregated** in Postgres (`freelancer_reputation`) and refreshed when a snapshot is **missing or older than five minutes** (configurable in code via `REPUTATION_SNAPSHOT_MAX_AGE_MS`). This keeps reads cheap at scale while staying close to real-time job data. |
| 4 | + |
| 5 | +## Metrics definitions |
| 6 | + |
| 7 | +| Field | Meaning | |
| 8 | +|--------|---------| |
| 9 | +| **completionRate** | `jobsCompleted / jobsStarted`, capped 0–1. `jobsStarted` counts all jobs where the user is `freelancer_id` (any non-null assignment). | |
| 10 | +| **disputeRate** | Share of those jobs that either have `status = 'disputed'` or at least one row in `disputes` for that `job_id`. | |
| 11 | +| **totalVolume** | Sum of `budget` over jobs with `status = 'completed'`. Amounts follow whatever `currency` is on each job; if you mix currencies, treat this as informational or extend the API to group by currency. | |
| 12 | +| **onTimeDeliveryPct** | Among completed jobs with both `deadline` and `completed_at`, the fraction where `completed_at <= deadline`. If none qualify, this is `null`. | |
| 13 | +| **reputationScore** | Optional display score 0–100: `0.4 * completion + 0.35 * onTime + 0.25 * (1 - dispute)`, with `0.5` used for on-time when there is no on-time sample but the user has started jobs. `null` when `jobsStarted === 0`. | |
| 14 | + |
| 15 | +Rates and the score are **`null`** when denominators are zero (except `totalVolume`, which is `0`). |
| 16 | + |
| 17 | +## Endpoints |
| 18 | + |
| 19 | +### 1. Public profile — `GET /api/freelancers/{userId}/reputation` |
| 20 | + |
| 21 | +- **Path**: `userId` is the integer primary key from `users.id`. |
| 22 | +- **Auth**: none. |
| 23 | +- **Cache**: `Cache-Control: public, s-maxage=60, stale-while-revalidate=300`. |
| 24 | +- **Errors**: `400` invalid id, `404` user missing, `503` database failure. |
| 25 | + |
| 26 | +**Example response** |
| 27 | + |
| 28 | +```json |
| 29 | +{ |
| 30 | + "userId": 42, |
| 31 | + "metrics": { |
| 32 | + "completionRate": 0.92, |
| 33 | + "disputeRate": 0.04, |
| 34 | + "totalVolume": "12500.00", |
| 35 | + "onTimeDeliveryPct": 0.88, |
| 36 | + "jobsStarted": 25, |
| 37 | + "jobsCompleted": 23, |
| 38 | + "jobsWithDispute": 1, |
| 39 | + "completedWithDeadline": 20, |
| 40 | + "onTimeDeliveries": 17 |
| 41 | + }, |
| 42 | + "reputationScore": 86.7, |
| 43 | + "computedAt": "2026-03-24T12:00:00.000Z" |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +### 2. Authenticated freelancer — `GET /api/freelancer/reputation` |
| 48 | + |
| 49 | +- **Auth**: session / access cookie (same as `/api/auth/me`). |
| 50 | +- **Resolution**: `users.wallet_address` must match the token’s wallet; returns that row’s reputation. |
| 51 | +- **Query**: `?refresh=1` or `?refresh=true` forces a recomputation before responding (use sparingly). |
| 52 | +- **Cache**: `Cache-Control: private, no-store`. |
| 53 | +- **Errors**: `401` unauthenticated, `404` no `users` row for wallet, `503` database failure. |
| 54 | + |
| 55 | +## Database setup |
| 56 | + |
| 57 | +Run the migration after `001-create-tables.sql` / `002-auth-tables.sql`: |
| 58 | + |
| 59 | +```bash |
| 60 | +# Example: pipe into psql or Neon's SQL editor |
| 61 | +scripts/003-freelancer-reputation.sql |
| 62 | +``` |
| 63 | + |
| 64 | +This adds `jobs.completed_at`, table `freelancer_reputation`, and indexes on `(freelancer_id, status)` and completed jobs to keep aggregations fast as data grows. |
| 65 | + |
| 66 | +## Frontend usage |
| 67 | + |
| 68 | +- **Profile pages**: call the public route with the profile’s numeric `userId`. |
| 69 | +- **Dashboard “my reputation”**: call `/api/freelancer/reputation` with `credentials: 'include'` (see existing dashboard fetch patterns in `lib/freelancer-dashboard.ts`). |
| 70 | + |
| 71 | +After job completion flows (including the Stellar worker), ensure `completed_at` is set so **on-time delivery** stays accurate; the worker updates it when marking a job `completed` from escrow release. |
0 commit comments