> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tktchurch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Logout

> Revoke access and refresh tokens, invalidating the current session

<Note>
  This endpoint requires authentication. Include the JWT access token in the Authorization header.
</Note>

### Request Body

<ParamField body="refresh_token" type="string" required={false}>
  Optional refresh token to revoke. If provided, both access and refresh tokens will be blacklisted.
</ParamField>

### Response

A successful request returns HTTP 204 No Content status. The following actions are performed:

1. The current access token is blacklisted
2. The refresh token is blacklisted (if provided)
3. The user's `validSince` timestamp is updated, invalidating all previous tokens
4. Expired blacklisted tokens are cleaned up from the database

### Error Responses

<ResponseField name="error" type="object">
  Error details when the request fails

  <Expandable title="Error Object">
    <ResponseField name="status" type="integer">
      HTTP status code
    </ResponseField>

    <ResponseField name="reason" type="string">
      Error message explaining why the request failed
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Common error cases:

  * 401 Unauthorized: Missing or invalid access token
  * 401 Unauthorized: Token has expired
  * 404 Not Found: User not found
</Note>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.tktchurch.com/v1/auth/logout" \
    -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
    -H "Content-Type: application/json" \
    -d '{
      "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
    }'
  ```

  ```javascript JavaScript theme={null}
  const logout = async (accessToken, refreshToken = null) => {
    const response = await fetch('https://api.tktchurch.com/v1/auth/logout', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
      },
      body: refreshToken ? JSON.stringify({ refresh_token: refreshToken }) : undefined
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error.reason);
    }

    return response.status === 204;
  };

  // Usage
  try {
    await logout(
      'eyJhbGciOiJIUzI1NiIs...', // access token
      'eyJhbGciOiJIUzI1NiIs...'  // refresh token (optional)
    );
    console.log('Logged out successfully');
  } catch (error) {
    console.error('Failed to logout:', error.message);
  }
  ```

  ```swift Swift theme={null}
  func logout(accessToken: String, refreshToken: String? = nil) async throws {
      var urlRequest = URLRequest(url: URL(string: "https://api.tktchurch.com/v1/auth/logout")!)
      urlRequest.httpMethod = "POST"
      urlRequest.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
      
      if let refreshToken = refreshToken {
          urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
          let body = ["refresh_token": refreshToken]
          urlRequest.httpBody = try? JSONEncoder().encode(body)
      }
      
      let (data, response) = try await URLSession.shared.data(for: urlRequest)
      guard let httpResponse = response as? HTTPURLResponse else {
          throw URLError(.badServerResponse)
      }
      
      if httpResponse.statusCode != 204 {
          let error = try JSONDecoder().decode(ErrorResponse.self, from: data)
          throw error
      }
  }

  // Usage
  do {
      try await logout(
          accessToken: "eyJhbGciOiJIUzI1NiIs...",
          refreshToken: "eyJhbGciOiJIUzI1NiIs..." // optional
      )
      print("Logged out successfully")
  } catch {
      print("Failed to logout: \(error.localizedDescription)")
  }
  ```

  ```kotlin Kotlin theme={null}
  class AuthService(private val client: OkHttpClient) {
      suspend fun logout(accessToken: String, refreshToken: String? = null) {
          val requestBody = refreshToken?.let {
              mapOf("refresh_token" to it).toJson()
                  .toRequestBody("application/json".toMediaType())
          }

          val request = Request.Builder()
              .url("https://api.tktchurch.com/v1/auth/logout")
              .post(requestBody ?: "".toRequestBody())
              .header("Authorization", "Bearer $accessToken")
              .apply {
                  refreshToken?.let {
                      header("Content-Type", "application/json")
                  }
              }
              .build()

          client.newCall(request).execute().use { response ->
              if (!response.isSuccessful) {
                  val error = response.body?.string()?.fromJson<ErrorResponse>()
                  throw Exception(error?.reason ?: "Failed to logout")
              }
          }
      }
  }

  // Usage
  try {
      authService.logout(
          "eyJhbGciOiJIUzI1NiIs...", // access token
          "eyJhbGciOiJIUzI1NiIs..."  // refresh token (optional)
      )
      println("Logged out successfully")
  } catch (e: Exception) {
      println("Failed to logout: ${e.message}")
  }
  ```

  ```typescript React Native theme={null}
  import axios from 'axios';

  interface LogoutRequest {
    refresh_token?: string;
  }

  const logout = async (accessToken: string, refreshToken?: string): Promise<boolean> => {
    try {
      const response = await axios.post(
        'https://api.tktchurch.com/v1/auth/logout',
        refreshToken ? { refresh_token: refreshToken } : undefined,
        {
          headers: {
            'Authorization': `Bearer ${accessToken}`,
            'Content-Type': 'application/json',
          },
        }
      );
      
      return response.status === 204;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        throw new Error(error.response?.data?.error?.reason || 'Failed to logout');
      }
      throw error;
    }
  };

  // Usage
  try {
    await logout(
      'eyJhbGciOiJIUzI1NiIs...', // access token
      'eyJhbGciOiJIUzI1NiIs...'  // refresh token (optional)
    );
    console.log('Logged out successfully');
  } catch (error) {
    console.error('Failed to logout:', error.message);
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 204 Success theme={null}
  // No content
  ```

  ```json 401 Unauthorized theme={null}
  {
    "error": {
      "status": 401,
      "reason": "Invalid or expired access token"
    }
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": {
      "status": 404,
      "reason": "User not found"
    }
  }
  ```
</ResponseExample>
