Appearance
REST API
The public REST API is mounted at /v1 (apps/mcp-server/src/rest/router.ts). Reads are anonymous (they power the webapp and crawlers); writes reuse the same Keycloak bearer token + scopes as the MCP tools and are self-scoped to the caller's own data. Every response carries permissive CORS (*) and the security headers.
For acquiring a token and calling authenticated endpoints, see Using the REST API.
Version & metadata
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/version | Server name + version | public |
| GET | /v1/meta/enums | Enum vocabularies (types, fuel, transmission, …) for UI builders | public |
Listings
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/listings | Search (type, make, model, year/price/mileage, fuel, transmission, condition, sort, limit, offset) | public |
| GET | /v1/listings/compare?ids=… | Compare 2–5 listings | public |
| GET | /v1/listings/:id | Fetch one (ETag caching) | public |
| GET | /v1/listings/:id/price-rating | Fair-price rating | public |
| GET | /v1/listings/:id/similar?limit=6 | Similar listings | public |
| POST | /v1/listings | Create | listings:write |
| POST | /v1/listings/bulk | Bulk create (≤50) | listings:write |
| PATCH | /v1/listings/:id | Update (partial) | listings:write |
| DELETE | /v1/listings/:id | Delete | listings:write |
| POST | /v1/listings/bulk-delete | Bulk delete (≤50) | listings:write |
| POST | /v1/search/nl | Natural-language search → filters + results | public |
Seller profiles (public)
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/dealers/:id | Public seller profile by id | public |
| GET | /v1/dealers/slug/:slug | Public seller profile by slug | public |
| GET | /v1/dealers/:id/reviews | Seller reviews + aggregate | public |
| POST | /v1/dealers/:id/reviews | Leave/update a review | listings:read |
My account — seller
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/me | My profile + locations (auto-provisions) | listings:read |
| PUT | /v1/me | Update my profile | listings:write |
| GET | /v1/me/listings?status=… | My listings (all statuses) | listings:read |
| GET | /v1/me/locations | My locations | listings:read |
| POST | /v1/me/locations | Add a location | listings:write |
| PATCH | /v1/me/locations/:id | Update a location | listings:write |
| DELETE | /v1/me/locations/:id | Delete a location | listings:write |
My account — buyer
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/me/account | Buyer profile + notification prefs | listings:read |
| PUT | /v1/me/account | Update buyer profile | listings:read |
| DELETE | /v1/me/account | Erase my account (all data + login) | listings:read |
| GET | /v1/me/favorites | My watchlist | listings:read |
| POST | /v1/me/favorites | Add to watchlist | listings:read |
| DELETE | /v1/me/favorites/:listingId | Remove from watchlist | listings:read |
| GET | /v1/me/saved-searches | My saved searches | listings:read |
| POST | /v1/me/saved-searches | Save a search (+ alerts) | listings:read |
| DELETE | /v1/me/saved-searches/:id | Delete a saved search | listings:read |
Account erasure is permanent
DELETE /v1/me/account is the GDPR / nDSG "right to erasure" endpoint. In one transaction it hard-deletes all of the caller's data — listings, seller profile + locations, buyer profile, favorites (theirs, and others' favorites of their listings), saved searches, reviews, org memberships and audit entries — then deletes their Keycloak login so the account can no longer sign in. It is self-scoped: a caller can only erase their own account. Returns 204. If the server has no account-deletion repository wired it returns 501; if no Keycloak admin service account is configured the data is still erased and only the login is left for an operator to remove. See Account deletion.
Organizations
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /v1/organizations | Create (caller becomes owner) | listings:write |
| GET | /v1/organizations/:id | Public org page by id | public |
| GET | /v1/organizations/slug/:slug | Public org page by slug | public |
| PATCH | /v1/organizations/:id | Update an org you own | listings:write |
| GET | /v1/me/organizations | My orgs + roles | listings:read |
| GET | /v1/organizations/:id/listings?status=… | Org inventory (member-only) | listings:read |
| GET | /v1/organizations/:id/members | List members (member-only) | listings:read |
| POST | /v1/organizations/:id/members | Add a member | listings:write |
| DELETE | /v1/organizations/:id/members/:userId | Remove a member | listings:write |
Geocoding & media
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /v1/geo/suggest?q=…&country=…&limit=5 | Forward geocoding for location pickers | public |
| POST | /v1/media | Upload an image/video (raw body) → { url, contentType } | listings:write |
Moderation & admin (listings:moderate)
| Method | Path | Purpose |
|---|---|---|
| PATCH | /v1/admin/listings/:id/moderation | Set listing moderation status |
| POST | /v1/admin/listings/:id/restore | Restore a soft-deleted listing |
| GET | /v1/admin/moderation?moderationStatus=…&includeDeleted=… | Moderation queue (all dealers) |
| GET | /v1/admin/audit?entityId=…&actorId=…&action=… | Append-only audit log |
| POST | /v1/admin/run-alerts | Trigger a saved-search alert pass (normally cron) |
| PATCH | /v1/admin/reviews/:id/moderation | Set review moderation status |
Liveness
| Method | Path | Purpose |
|---|---|---|
| GET | /healthz | Liveness |
| GET | /readyz | Readiness |