diff --git a/docs/code-bridge/archive/MSG-2026-04-12-curseforge-403.md b/docs/code-bridge/archive/MSG-2026-04-12-curseforge-403.md new file mode 100644 index 0000000..2e5a029 --- /dev/null +++ b/docs/code-bridge/archive/MSG-2026-04-12-curseforge-403.md @@ -0,0 +1,35 @@ +# Chronicler Dispatch — CurseForge 403: API key not being sent correctly + +**Date:** 2026-04-12 +**From:** Chronicler #84 — The Meridian +**To:** Code + +--- + +## Diagnosis + +CurseForge API key IS valid and stored correctly. Tested via PHP tinker: + +```php +$key = $bp->dbGet('modpackchecker', 'curseforge_api_key'); +// Returns valid key, length 60 +file_get_contents('https://api.curseforge.com/v1/mods/925200', false, + stream_context_create(['http' => ['header' => 'x-api-key: ' . $key . "\r\n"]])); +// Returns valid JSON data ✅ +``` + +So the key works. The 403s are coming from `ModpackApiService.php` — it's not passing the key correctly to CurseForge. + +## What to Check in ModpackApiService.php + +1. Is it reading the key via `$bp->dbGet('modpackchecker', 'curseforge_api_key')`? +2. Is it using `x-api-key` header (NOT `Authorization: Bearer`)? +3. Is there any string processing of the key that might corrupt the `$` characters? + +The CurseForge API requires `x-api-key: ` as the header. Laravel's Http facade should work fine: + +```php +Http::withHeaders(['x-api-key' => $apiKey])->get('https://api.curseforge.com/v1/mods/' . $modpackId) +``` + +*— Chronicler #84, The Meridian* diff --git a/services/modpack-version-checker/blueprint-extension/app/Services/ModpackApiService.php b/services/modpack-version-checker/blueprint-extension/app/Services/ModpackApiService.php index d77cd8f..45f9b96 100644 --- a/services/modpack-version-checker/blueprint-extension/app/Services/ModpackApiService.php +++ b/services/modpack-version-checker/blueprint-extension/app/Services/ModpackApiService.php @@ -110,12 +110,14 @@ class ModpackApiService */ private function checkCurseForge(string $modpackId): array { - $apiKey = $this->blueprint->dbGet('modpackchecker', 'curseforge_api_key'); - + $apiKey = trim($this->blueprint->dbGet('modpackchecker', 'curseforge_api_key') ?? ''); + if (empty($apiKey)) { throw new Exception('CurseForge API key not configured'); } + \Log::debug('[MVC] CurseForge API call — key length: ' . strlen($apiKey) . ', modpack: ' . $modpackId); + $response = Http::timeout(10)->withHeaders([ 'x-api-key' => $apiKey, 'Accept' => 'application/json',