Search for a command to run...
After a store admin configures an integration, a consumer reads its options at runtime. The consumer is often the same package that declared the integration, such as a payment provider reading its own credentials, but it can be any other code, like an API route, a subscriber, or a scheduled job.
The API you use depends on where your code runs.
A payment or fulfillment provider runs inside its own module's isolated container, so it cannot resolve the Integration Module directly. It uses , which runs a workflow that reaches the app container. Code that already holds the container resolves the module and calls directly, without a workflow.
Use the helper from a provider that cannot reach the Integration Module directly.
Pass the identifier your integration declares, and a type parameter for typed options. For example:
providers/payment-acme/services/acme-payment.ts1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"2import type { AcmeOptions } from "../../../integration-acme/service"34// inside any provider method5const options = await resolveIntegrationOptions<AcmeOptions>({6 identifier: "acme"7})
The returned is typed as , already decrypted and validated.
For a provider that supports multiple instances, pass . It is usually your provider's own registration id. Omit it, or pass , for the default instance:
providers/payment-acme/services/acme-payment.ts1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"2import type { AcmeOptions } from "../../../integration-acme/service"34// inside any provider method5const options = await resolveIntegrationOptions<AcmeOptions>({6 identifier: "acme",7 instance_id: this.instanceId_,8})
By default, the helper throws a when the integration is not configured, disabled, or incomplete:
1// throws if the integration isn't usable yet2const options = await resolveIntegrationOptions<AcmeOptions>({3 identifier: "acme"4})
Pass to receive instead, so you can handle the missing case yourself:
1const options = await resolveIntegrationOptions<AcmeOptions>(2 { identifier: "acme" },3 { optional: true }4)56if (!options) {7 // not configured yet: skip, fall back, or return a friendly error8}
When your code already has the container, resolve the module and call directly. This suits API routes, subscribers, scheduled jobs, and loaders, and no workflow is involved. For example, in an API route:
api/admin/acme-payment/route.ts1import { INTEGRATION_MODULE, IntegrationModuleService } from "@gorgo/medusa-integration"2import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"34export async function GET(req: MedusaRequest, res: MedusaResponse) {5 const integration: IntegrationModuleService = req.scope.resolve(INTEGRATION_MODULE)67 const resolved = await integration.getResolvedOptions("acme")8 if (!resolved) {9 return res.status(503).send("Acme is not configured")10 }1112 const options = resolved.options as AcmeOptions13 const { provider_id, category, is_enabled } = resolved.meta14}
returns . A value is , where holds , , and . A result means the integration is not configured, disabled, or incomplete.
When you compose your own workflow, use the exported step instead of the helper:
workflows/create-acme.ts1import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"2import { getResolvedIntegrationOptionsStep } from "@gorgo/medusa-integration"34export const createAcmeWorkflow = createWorkflow("create-acme", () => {5 const resolved = getResolvedIntegrationOptionsStep({6 identifier: "acme"7 })8 return new WorkflowResponse(resolved)9})
The standalone is also exported. It is what the helper runs internally.
Resolved options are not the raw stored values. On every resolve, the module does three things:
If the integration is not configured, disabled, or incomplete, it resolves to . The helper throws in that case, or returns with . Partial drafts never reach runtime.
Resolved options are cached in memory for a short time, so hot paths do not re-read and decrypt on every call. The cache is invalidated whenever the integration changes, whether it is saved, enabled, disabled, or deleted, so consumers pick up new configuration without a redeploy.