TAG API — USER FOLLOWS, FOLLOW REQUESTS & BLOCKS
==================================================
Base URL : https://tag.nsamaandcompany.com/tag_api
Auth     : Authorization: Bearer <token>  required on all endpoints


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PUBLIC USER PROFILE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

GET  /api/v1/users/{tagId}/profile              [AUTH]
  Fetch any user's public profile by their public_tag_id.
  Path param: tagId — the user's public_tag_id (e.g. "johndoe")
  Returns:
    {
      id, public_tag_id, full_name, avatar_url,
      is_private, follower_count, following_count, vibes_count,
      is_following,
      follow_request_status  (null | "pending" | "accepted" | "rejected")
    }
  Notes:
    - follow_request_status is only present when is_private = true
    - Use is_private + follow_request_status to decide which button to render:
        is_private=false              → show "Follow" (sends direct follow)
        is_private=true + null        → show "Request to Follow"
        is_private=true + pending     → show "Requested" (disabled)
        is_private=true + accepted    → show "Following"


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FOLLOW SUGGESTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

GET  /api/v1/users/suggestions                  [AUTH]
  Returns people you might want to follow (mutual contacts / shared activity).
  Returns: [
    {
      user_id, public_tag_id, full_name, avatar_url,
      is_private, mutual_count,
      follow_request_status  (null | "pending" | "accepted" | "rejected")
    }
  ]


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FOLLOW / UNFOLLOW
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

POST /api/v1/users/{userId}/follow              [AUTH]
  Follow a user.
  Path param: userId — the user's numeric id
  Body: (none)

  Behaviour depends on whether the target account is public or private:

  Public account → direct follow
    Response 200: { "is_following": true }

  Private account → sends a follow request
    Response 201: { "is_following": false, "request_sent": true, "request_id": 7 }
    Notes:
      - FCM push sent to target: type = "follow_request"
      - 422 if a pending request already exists

DELETE /api/v1/users/{userId}/follow            [AUTH]
  Unfollow a user.
  Body: (none)
  Response 200: { "is_following": false }


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FOLLOWERS & FOLLOWING LISTS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

GET  /api/v1/users/{tagId}/followers            [AUTH]
  List everyone who follows the user identified by tagId.
  Path param: tagId — the user's public_tag_id
  Returns: [
    { user_id, public_tag_id, full_name, avatar_url, is_following }
  ]
  Note: is_following indicates whether the authenticated caller follows each person.

GET  /api/v1/users/{tagId}/following            [AUTH]
  List everyone the user identified by tagId is following.
  Path param: tagId — the user's public_tag_id
  Returns: [
    { user_id, public_tag_id, full_name, avatar_url, is_following }
  ]


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FOLLOW REQUESTS  (private accounts only)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

GET  /api/v1/users/follow-requests              [AUTH]
  List all incoming PENDING follow requests for the authenticated user.
  Response 200: [
    {
      id, created_at,
      sender: { user_id, full_name, public_tag_id, avatar_url }
    }
  ]

PUT  /api/v1/users/follow-requests/{id}/accept  [AUTH]
  Accept a follow request. Creates the follow relationship automatically.
  Path param: id — the follow request id
  Response 200: { "request_id": 7 }
  FCM push to sender: type = "follow_request_accepted"
  Errors:
    404 — request not found or you are not the receiver
    422 — request already accepted or rejected

PUT  /api/v1/users/follow-requests/{id}/reject  [AUTH]
  Reject a follow request.
  Path param: id — the follow request id
  Response 200: { "request_id": 7 }
  Errors:
    404 — request not found or you are not the receiver
    422 — request already accepted or rejected

DELETE /api/v1/users/{userId}/follow-requests   [AUTH]
  Cancel a pending follow request you sent to userId.
  Path param: userId — the user you sent the request to
  Response 200: { "is_following": false, "request_sent": false }
  Errors:
    404 — no pending request found


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FCM PUSH TYPES — FOLLOW SYSTEM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

type = "follow_request"
  Sent to target when someone requests to follow them.
  Payload: { type, request_id }

type = "follow_request_accepted"
  Sent to sender when their request is accepted.
  Payload: { type, request_id }


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BLOCKS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

GET  /api/v1/blocks                             [AUTH]
  List all users you have blocked.
  Returns: [
    { user_id, full_name, public_tag_id, avatar_url, blocked_at }
  ]

POST /api/v1/users/{userId}/block               [AUTH]
  Block a user. Any existing DM conversation is automatically archived.
  Path param: userId — numeric id of the user to block
  Body: (none)
  Response 201: { "message": "User blocked." }
  Errors:
    422 — cannot block yourself
    404 — user not found

DELETE /api/v1/users/{userId}/block             [AUTH]
  Unblock a previously blocked user.
  Path param: userId — numeric id of the user to unblock
  Body: (none)
  Response 200: { "message": "User unblocked." }

Notes:
  - Blocking a user does not automatically remove them from followers/following.
  - Blocked users cannot send you DMs (they will receive an error on send).
  - The archived conversation is not automatically restored on unblock.
