Skip to content

Staff Moderation

Staff moderation endpoints allow admin clients to review and update libraries, reports, and community photo submissions through the API.

All endpoints require a JWT access token for a user with is_staff=true. Authenticated non-staff users receive:

{
  "message": "Staff access required."
}

with status 403 Forbidden.

Summary

GET /api/v1/libraries/moderation/summary

Return moderation dashboard counts for staff clients.

Auth required: Yes (Bearer token, staff account)

{
  "pending_libraries_count": 4,
  "open_reports_count": 2,
  "pending_photos_count": 5,
  "total_pending": 11,
  "total_libraries": 350,
  "total_users": 128
}

List libraries

GET /api/v1/libraries/moderation

Return a paginated staff list of libraries across all moderation statuses. Approved libraries with staged owner edits appear as pending items using their proposed values, while their persisted public values remain approved.

Auth required: Yes (Bearer token, staff account)

Query parameters

Parameter Type Default Description
status string all all, pending, approved, or rejected
q string Search name, address, or city
country string Filter by ISO 3166-1 alpha-2 country code
source string Filter by source text
page int 1 Page number (1–1000)
page_size int 20 Items per page (1–50)

GET /api/v1/libraries/moderation/pending is also available as a convenience shortcut for new pending submissions and staged edits to approved libraries. It accepts the same q, country, source, page, and page_size parameters.

Response (200 OK)

Each item uses the standard library response fields plus status, rejection_reason, and created_by.

{
  "items": [
    {
      "id": 42,
      "slug": "florence-via-rosina-15-corner-books",
      "name": "Corner Books",
      "description": "A cozy community bookcase near the park entrance.",
      "photo_url": "/media/libraries/photos/corner-books.jpg",
      "thumbnail_url": "/media/libraries/thumbnails/corner-books.jpg",
      "lat": 43.7696,
      "lng": 11.2558,
      "address": "Via Rosina 15",
      "city": "Florence",
      "country": "IT",
      "postal_code": "50123",
      "wheelchair_accessible": "",
      "capacity": null,
      "is_indoor": null,
      "is_lit": null,
      "website": "",
      "contact": "",
      "source": "",
      "operator": "",
      "brand": "",
      "created_at": "2026-06-15T14:30:00Z",
      "is_favourited": false,
      "status": "pending",
      "rejection_reason": "",
      "created_by": {
        "id": 1,
        "username": "janedoe"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 1,
    "total_pages": 1,
    "has_next": false,
    "has_previous": false
  }
}

Get a library

GET /api/v1/libraries/moderation/{slug}

Return any library by slug for staff users, including pending and rejected libraries hidden from the public detail endpoint. When an approved library has staged edits, this endpoint returns the proposed values with status: "pending" for review.

Update library status

PATCH /api/v1/libraries/moderation/{slug}

Set a library moderation status by slug. For a new pending submission, this updates the library status normally. For staged edits to an approved library, approved applies the proposed fields and photo to the existing library, while rejected discards only the proposed update and leaves the existing library approved. Sending pending for an already staged edit is idempotent: it returns the pending preview with 200 OK without changing the approved row, staged payload, or update timestamp.

Auth required: Yes (Bearer token, staff account)

Field Type Required Description
status string Yes New status: pending, approved, or rejected. pending is a no-op when an edit is already staged.
rejection_reason string No Optional reason stored when rejecting the library
curl -X PATCH https://bookcorners.org/api/v1/libraries/moderation/florence-via-rosina-15-corner-books \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"status": "approved"}'
curl -X PATCH https://bookcorners.org/api/v1/libraries/moderation/florence-via-rosina-15-corner-books \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"status": "rejected", "rejection_reason": "Duplicate submission."}'

Returns the updated library using the same response shape as the library moderation list.

List reports

GET /api/v1/libraries/moderation/reports

Return a paginated staff list of user-submitted reports.

Auth required: Yes (Bearer token, staff account)

Parameter Type Default Description
status string all all, open, resolved, or dismissed
reason string all all, damaged, missing, incorrect_info, inappropriate, or other
page int 1 Page number (1–1000)
page_size int 20 Items per page (1–50)

Each report includes the target library summary, reporter summary, reason, details, optional photo_url, status, and created_at.

Update report status

PATCH /api/v1/libraries/moderation/reports/{report_id}

Set a report status.

Field Type Required Description
status string Yes New status: open, resolved, or dismissed
curl -X PATCH https://bookcorners.org/api/v1/libraries/moderation/reports/7 \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}'

List community photos

GET /api/v1/libraries/moderation/photos

Return a paginated staff list of community photo submissions.

Auth required: Yes (Bearer token, staff account)

Parameter Type Default Description
status string all all, pending, approved, or rejected
page int 1 Page number (1–1000)
page_size int 20 Items per page (1–50)

Each photo includes the parent library summary, submitter summary, caption, photo_url, thumbnail_url, status, and created_at.

Update community photo status

PATCH /api/v1/libraries/moderation/photos/{photo_id}

Set a community photo status. Approved photos are promoted to the parent library's primary image by the model.

Field Type Required Description
status string Yes New status: pending, approved, or rejected
curl -X PATCH https://bookcorners.org/api/v1/libraries/moderation/photos/12 \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"status": "approved"}'

Errors

Status Cause
401 Missing or invalid authentication token
403 Authenticated user is not staff
404 Library, report, or photo does not exist
422 Invalid payload or unsupported status/filter values
429 Rate limit exceeded (see Rate Limiting)