{"record":{"id":"ec99e8955fe34ca8","repo":"bitwarden/server","slug":"invalid-billing-sync-key-ec99e8","errorCode":null,"errorMessage":"Invalid Billing Sync Key","messagePattern":"Invalid Billing Sync Key","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs","lineNumber":222,"sourceCode":"\n        await _setUpSponsorshipCommand.SetUpSponsorshipAsync(\n            sponsorship,\n            await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));\n\n        _logger.LogInformation(\n            \"Sponsorship redemption succeeded: SponsorshipId={SponsorshipId}, SponsoredOrganizationId={SponsoredOrganizationId}\",\n            sponsorship.Id,\n            model.SponsoredOrganizationId);\n    }\n\n    [Authorize(\"Installation\")]\n    [HttpPost(\"sync\")]\n    public async Task<OrganizationSponsorshipSyncResponseModel> Sync([FromBody] OrganizationSponsorshipSyncRequestModel model)\n    {\n        var sponsoringOrg = await _organizationRepository.GetByIdAsync(model.SponsoringOrganizationCloudId);\n        if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(sponsoringOrg, model.BillingSyncKey))\n        {\n            throw new BadRequestException(\"Invalid Billing Sync Key\");\n        }\n\n        var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);\n        await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.DisplayName(), offersToSend);\n        return new OrganizationSponsorshipSyncResponseModel(syncResponseData);\n    }\n\n    [Authorize(\"Application\")]\n    [HttpDelete(\"{sponsoringOrganizationId}\")]\n    [SelfHosted(NotSelfHostedOnly = true)]\n    public async Task RevokeSponsorship(Guid sponsoringOrganizationId)\n    {\n\n        var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);\n        if (_currentContext.UserId != orgUser?.UserId)\n        {\n            throw new BadRequestException(\"Can only revoke a sponsorship you granted.\");\n        }","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Billing/Controllers/OrganizationSponsorshipsController.cs#L204-L240","documentation":"Thrown (HTTP 400) on POST /organization-sponsorships/sync (Installation-authorized) when _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync returns false for the sponsoring organization. The self-hosted installation must present the correct billing sync key to sync sponsorship data.","triggerScenarios":"Self-hosted installation sends a stale or wrong billing sync key; the key was regenerated in the cloud portal; the sponsoring org id in the request does not match the configured org.","commonSituations":"Configuration drift between cloud billing settings and the self-hosted installation's billing sync configuration.","solutions":["Copy the current billing sync key from the cloud organization's billing settings.","Update the self-hosted installation configuration with the correct key.","Verify the SponsoringOrganizationCloudId in the request is correct."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Installation client: verify the billing sync key is configured and current before syncing.\nif (!model.billingSyncKey) throw new Error('Billing sync key required for sponsorship sync');\nif (!model.sponsoringOrganizationCloudId) throw new Error('Sponsoring org cloud id required');","typeGuard":null,"tryCatchPattern":"try {\n  await post('/organization-sponsorships/sync', syncModel);\n} catch (e) {\n  if (e.isBadRequest && /invalid billing sync key/i.test(e.message)) {\n    await refreshBillingSyncKey(); // re-fetch from cloud, retry once\n  } else { throw e; }\n}","preventionTips":["Keep the billing sync key in managed config shared between cloud and self-hosted.","Rotate keys deliberately and update the installation atomically.","Verify the SponsoringOrganizationCloudId matches the configured org."],"tags":["billing","sponsorship","sync","authentication","secret","self-hosted"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}