Skip to main content
PATCH
/
api
/
threads
/
{id}
/
visibility
curl -X PATCH 'http://localhost:5079/api/threads/f47ac10b-58cc-4372-a567-0e02b2c3d479/visibility' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"visibility": "public"}'
{
  "success": true,
  "message": "Thread visibility updated successfully",
  "visibility": "public"
}
Note: This endpoint was previously documented as GET /api/threads/{id}/share but the actual backend implementation is PATCH /api/threads/{id}/visibility (Lines 338-420 in ThreadsController.cs). The /share endpoint does NOT exist in the current backend.

Authentication

Required: JWT Bearer token JWT Claims Extraction (Lines 346-350):
sub | ClaimTypes.NameIdentifier → User UUID (required)

Path Parameters

id
string
required
Thread UUIDFormat: Valid GUIDValidation: Route constraint :guid (Line 339)

Request Body

visibility
string
required
New visibility levelValidation (Lines 381-400):
if (string.IsNullOrWhiteSpace(request?.Visibility)) {
    return BadRequest("Visibility is required");
}

var validVisibilities = new[] { "private", "public", "unlisted" };
if (!Array.Exists(validVisibilities, v => v == request.Visibility.ToLowerInvariant())) {
    return BadRequest("Invalid visibility");
}
Allowed Values:
  • "private": Owner-only access
  • "public": Public access when public_sharing feature enabled
  • "unlisted": Accessible via direct link when public_sharing feature enabled
Case-Insensitive: Automatically lowercased (Line 392)
curl -X PATCH 'http://localhost:5079/api/threads/f47ac10b-58cc-4372-a567-0e02b2c3d479/visibility' \
  -H 'Authorization: Bearer YOUR_JWT_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"visibility": "public"}'
{
  "success": true,
  "message": "Thread visibility updated successfully",
  "visibility": "public"
}

Authorization

Ownership Verification (Lines 358-378):
var thread = await _threadsService.GetThreadAsync(threadId);
if (thread == null) {
    return NotFound("Thread not found");
}

if (thread.UserId != userId) {
    return StatusCode(403, "Only the thread owner can change visibility");
}
Permission Rules:
  • ONLY thread owner can change visibility (Line 369)
  • Explicit message: “Only the thread owner can change visibility” (Line 375)
  • No delegation or admin override

Side Effects

Database Mutations (Line 402):
await _threadsService.UpdateThreadVisibilityAsync(threadId, request.Visibility);
Tables Written:
  • UPDATE threads SET visibility = {request.Visibility}, updated_at = NOW() WHERE thread_id = {threadId}
Access Control Changes:
  • Changing TO "private": Thread becomes owner-only
  • Changing TO "public": Thread becomes publicly accessible (if public_sharing feature enabled)
  • Changing TO "unlisted": Thread accessible via direct link (if public_sharing feature enabled)
Cascade Effects: None
  • Existing messages/comparisons/votes unaffected
  • Thread remains in owner’s thread list
  • Share URLs become invalid/valid based on new visibility

Permissions

Who Can Modify Visibility:
  • Thread owner only
Who Cannot Modify:
  • Other authenticated users
  • Public thread viewers
  • Admins (not documented as exception)

Visibility Semantics

private

  • Access: Owner only, always
  • Feature Flag: Irrelevant (owner-only regardless)
  • Sharing: Cannot be shared

public

  • Access: Anyone if public_sharing = true, owner only if false
  • Discovery: May be listed in public directories (implementation-dependent)
  • Indexing: May be indexed by search engines

unlisted

  • Access: Anyone with link if public_sharing = true, owner only if false
  • Discovery: Not listed publicly
  • Indexing: Implementation-dependent

Edge Cases

  1. Thread doesn’t exist: 404 (Lines 359-367)
  2. User is not owner: 403 (Lines 369-378)
  3. Visibility is null: 400 (Lines 381-389)
  4. Visibility is empty: 400 (Lines 381-389)
  5. Invalid visibility value: 400 (Lines 391-400)
  6. Case variations ("Public", "PUBLIC"): Accepted, lowercased (Line 392)
  7. Same visibility as current: Update proceeds (no change detection)
  8. Thread currently has public viewers: Changing to private immediately denies access

Error Conditions

CodeHTTPCauseController Line
N/A401JWT missing or invalidMiddleware
N/A401User ID claim missing352-355
INVALID_REQUEST400Visibility null/empty381-389
INVALID_REQUEST400Invalid visibility value391-400
NOT_FOUND404Thread doesn’t exist359-367
FORBIDDEN403Not thread owner369-378
VISIBILITY_UPDATE_ERROR500Service exception411-419
Exception Handling (Lines 411-419):
catch (Exception ex) {
    return StatusCode(500, new { error = ex.Message, code = "VISIBILITY_UPDATE_ERROR" });
}

Behavioral Guarantees

Atomicity: Single UPDATE query (atomic) Idempotency: NOT idempotent
  • updated_at timestamp changes on every call
  • Even if visibility unchanged
Immediate Effect: Visibility change takes effect immediately
  • Next GET request reflects new visibility
  • Access control updated instantly

Validation Order

  1. User ID from JWT (401 if missing)
  2. Thread existence (404 if not found)
  3. Ownership (403 if not owner)
  4. Visibility value validation (400 if invalid)
  5. Update execution (500 if fails)
Note: Validation happens AFTER ownership check

Security Implications

Public Exposure Risk:
  • Changing private → public exposes thread to world (if feature enabled)
  • No confirmation required
  • No warning for sensitive content
Privacy Downgrade:
  • Private → public/unlisted is one-way exposure
  • Changing back to private doesn’t “un-share” (content may be cached elsewhere)
Access Revocation:
  • Changing public → private immediately denies access to non-owners
  • No grace period

Feature Flag Dependency

Depends on: public_sharing feature flag Behavior Matrix:
FlagVisibilityEffect on Access
ONprivateOwner only
ONpublicPublic access
ONunlistedLink-based access
OFFprivateOwner only
OFFpublicOwner only (flag overrides visibility)
OFFunlistedOwner only (flag overrides visibility)
Note: Setting visibility to public/unlisted has NO EFFECT if public_sharing = false

Response Format

Success Response (Lines 404-409):
  • Includes visibility field echoing normalized value (lowercased)
  • Example: Request "PUBLIC" → Response "public"