API is throwing 404 errors

Hello Discourse Community,

I’m currently facing challenges with Discourse API endpoints, specifically related to suspending users, and would greatly appreciate any assistance or guidance you can offer.

Summary of Issues

  1. 404 Not Found:

    • When using the endpoint:
      /admin/users/15/suspend.json, I consistently receive a “Page Not Found” error, even though the user exists.
    • I’ve also tried the alternative endpoint:
      /admin/u/15/suspend.json, which results in the same issue.
  2. Unexpected HTML Response:

    • Instead of returning the expected JSON response, the API returns an HTML page with the message:
      “Oops! That page doesn’t exist or is private.”
    • This occurs regardless of whether I’m using curl or PowerShell for the request.
  3. API Key and Permissions:

    • The API key being used has full admin access, yet the requests continue to return a 404 error.
    • I’ve also tested using granular keys (restricted to suspension actions) and a read-only key, but the issue persists.
    • Interestingly, the endpoint works perfectly when accessed via a browser, but fails when using API requests with the key.

Example Requests

curl Example

curl -X PUT "https://your-discourse-domain.com/admin/users/15/suspend.json" \
-H "Api-Key: [REDACTED_API_KEY]" \
-H "Api-Username: system" \
-H "Content-Type: application/json" \
-d '{"suspend_until": "2024-12-31", "reason": "Violation of community guidelines"}'

PowerShell Example

# PowerShell Script to Suspend a User on Discourse

$baseUrl = "https://your-discourse-domain.com"
$userId = 15
$apiKey = "[REDACTED_API_KEY]"
$apiUsername = "system"
$suspendUntil = "2024-12-31"
$reason = "Violation of community guidelines"

$headers = @{
    "Api-Key" = $apiKey
    "Api-Username" = $apiUsername
    "Content-Type" = "application/json"
}

$body = @{
    suspend_until = $suspendUntil
    reason = $reason
} | ConvertTo-Json

$endpoint = "$baseUrl/admin/users/$userId/suspend.json"

try {
    $response = Invoke-RestMethod -Uri $endpoint -Method Put -Headers $headers -Body $body -ErrorAction Stop
    Write-Host "User suspended successfully!" -ForegroundColor Green
} catch {
    Write-Host "Error suspending user:" -ForegroundColor Red
    Write-Host $_.Exception.Message
}

Additional Details

  • Discourse Version: 3.4.0.beta3
  • Troubleshooting Steps Taken:
    I’ve tried adjusting request headers, including User-Agent and Referer, but to no avail.

Any insights or suggestions to help resolve these issues would be greatly appreciated. Thank you in advance for your time and support!

Best regards,
Ross

Your example works as-is modulo the expected parameters against our test site:

○ → curl -i -X PUT "https://try.discourse.org/admin/users/41/suspend.json" \
  -H "Api-Key: swordfish" \
  -H "Api-Username: michael" \
  -H "Content-Type: application/json" \
  -d '{"suspend_until": "2024-12-31", "reason": "Violation of community guidelines"}'

HTTP/2 200 
server: nginx
date: Mon, 11 Nov 2024 19:27:44 GMT
content-type: application/json; charset=utf-8
vary: Accept-Encoding
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
x-discourse-username: michael
x-discourse-route: users/suspend
cache-control: no-cache, no-store
x-request-id: fe9c5ddc-b11a-45ba-ab41-9403eb53f255
cdck-proxy-id: app-router-tiehunter03.sea1
strict-transport-security: max-age=31536000
cdck-proxy-id: app-balancer-tieinterceptor1b.sea1

{
  "suspension": {
    "suspend_reason": "Violation of community guidelines",
    "full_suspend_reason": "Violation of community guidelines",
    "suspended_till": "2024-12-31T00:00:00.000Z",
    "suspended_at": "2024-11-11T19:27:44.927Z",
    "suspended_by": {
      "id": 85,
      "username": "michael",
      "name": "Michael Brown",
      "avatar_template": "/user_avatar/try.discourse.org/michael/{size}/639_2.png"
    }
  }
}
  • validate the API key
    does Discourse show a valid user authentication (x-discourse-username) when making a simple GET with that key?
  • validate the user ID
    it really exists, right? it’ll 404 if it doesn’t
  • are you behind Cloudflare?
    it can mess with parameters
2 Likes
  1. API Key Validation
    The API key was confirmed to be valid using the following command:

    curl -X GET "https://redactedurl.com/admin/users/list/active.json" -H "Api-Key: LIVEPRODKEYHERE" -H "Api-Username: system"
    

    The response (with sensitive data redacted for confidentiality) is as follows:

    [
      {
        "id": 1,
        "username": "User1",
        "name": "Redacted",
        "avatar_template": "/user_avatar/yourforum.com/user1/{size}/avatar.png",
        "active": true,
        "admin": true,
        "moderator": true,
        "trust_level": 4,
        "title": "Recognized Member",
        "days_visited": 6,
        "post_count": 3
      },
      {
        "id": 19,
        "username": "User2",
        "name": "Redacted",
        "avatar_template": "/letter_avatar_proxy/v4/letter/j/{size}.png",
        "active": true,
        "admin": false,
        "moderator": false,
        "trust_level": 1,
        "title": "Member (Unverified)"
      },
      {
        "id": 3,
        "username": "User3",
        "name": "Redacted",
        "avatar_template": "/user_avatar/yourforum.com/user3/{size}/avatar.png",
        "active": true,
        "admin": true,
        "moderator": true,
        "trust_level": 4,
        "title": "Community Founder"
      },
      {
        "id": -1,
        "username": "system",
        "name": "system",
        "avatar_template": "/user_avatar/yourforum.com/system/{size}/avatar.png",
        "active": true,
        "admin": true,
        "moderator": true,
        "trust_level": 4,
        "title": "Bot"
      }
    ]
    
  2. User Existence Confirmation
    The test user (ID: 15) was verified to exist, as demonstrated by querying the user’s data using /admin/users/15/suspend.json.:

    {
      "id": 15,
      "username": "User15",
      "active": true,
      "admin": false,
      "moderator": false,
      "trust_level": 0,
      "days_visited": 1,
      "post_count": 0,
      "can_send_activation_email": true,
      "can_activate": false,
      "can_deactivate": true,
      "can_grant_admin": true,
      "can_grant_moderation": true,
      "can_impersonate": true,
      "groups": [
        {
          "name": "trust_level_0",
          "bio_excerpt": "New members with limited abilities"
        }
      ]
    }
    

  1. Network Configuration
    The site is not utilizing Cloudflare. Instead, it operates behind an NGINX reverse proxy. The application listens on port 8080 internally, with NGINX handling SSL termination on port 443 for external access. The app.yml configuration does not bind directly to port 443, as SSL and certificate management are handled by NGINX to ensure secure connectivity and availability.