medusajs/medusa · error · MedusaError
Sales channels can only be associated with publishable API k
Error message
Sales channels can only be associated with publishable API keys
What it means
The POST /admin/api-keys/:id/sales-channels route only allows linking sales channels to publishable API keys. The route refetches the key's type and rejects with INVALID_DATA (HTTP 400) when the key is a secret key.
Source
Thrown at packages/medusa/src/api/admin/api-keys/[id]/sales-channels/route.ts:21
import { ApiKeyType, MedusaError } from "@medusajs/framework/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { refetchApiKey } from "../../helpers"
export const POST = async (
req: AuthenticatedMedusaRequest<
HttpTypes.AdminBatchLink,
HttpTypes.SelectParams
>,
res: MedusaResponse<HttpTypes.AdminApiKeyResponse>
) => {
const { add, remove } = req.validatedBody
const apiKey = await refetchApiKey(req.params.id, req.scope, ["id", "type"])
if (apiKey.type !== ApiKeyType.PUBLISHABLE) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Sales channels can only be associated with publishable API keys"
)
}
await linkSalesChannelsToApiKeyWorkflow(req.scope).run({
input: {
id: req.params.id,
add,
remove,
},
})
const updatedApiKey = await refetchApiKey(
req.params.id,
req.scope,
req.queryConfig.fields
)View on GitHub (pinned to 5e06e544a2)
Solutions
- Create a publishable API key (POST /admin/api-keys with type=publishable) and link channels to that
- Filter key lists by type=publishable in your admin UI before showing the link action
- Check the key's type via GET /admin/api-keys/:id before calling
Example fix
// before
const key = await sdk.apiKey.create({ title: "store", type: "secret" })
await sdk.apiKey.linkSalesChannels(key.id, { add: [scId] }) // 400
// after
const key = await sdk.apiKey.create({ title: "store", type: "publishable" })
await sdk.apiKey.linkSalesChannels(key.id, { add: [scId] }) Defensive patterns
Strategy: validation
Validate before calling
const { api_key } = await sdk.apiKey.retrieve(keyId, { fields: ["id","type"] })
if (api_key.type !== "publishable") {
throw new Error("Link sales channels only to publishable keys")
} Type guard
const isPublishableKey = (k: { type: string }) => k.type === "publishable" Try / catch
try { await link(keyId, payload) } catch (e) { if (e.type === "invalid_data" && /publishable/.test(e.message)) { /* recreate key as publishable */ } throw e } Prevention
- Filter API key lists by type before offering link actions
- Create publishable keys for storefront scoping from the start
When it happens
Trigger: POST /admin/api-keys/:id/sales-channels with { add, remove } where the target key has type 'secret'; attempting to reuse secret-key management endpoints as if they were publishable-key endpoints.
Common situations: UI dropdown listing all API keys without filtering by type; automation scripts that assume any key can be scoped to sales channels; creating the key with the wrong type initially.
Related errors
- Nonexistent relations were passed during upsert: ${nonexiste
- Many-to-one relation ${relation.name} must be set with an ID
- Campaign with id: ${req.params.id} was not found
- Campaign with id "${req.params.id}" not found
- Claim with id: ${req.params.id} was not found
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/67599e391793985d.
Report an issue: GitHub.