Command Palette

Search for a command to run...

Reading Options

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.

What you get back is resolved: the module has decrypted and validated the options, and applied their defaults. See What Resolved Means for the details.

Two Ways to Read

The API you use depends on where your code runs.

Where your code runsHow to read
A provider in an isolated module container, or a module service
Code with the app or request container (API routes, subscribers, scheduled jobs, loaders)
A step inside a workflow you compose

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 to reach the app container. Code that already holds the container resolves the module and calls directly, without a workflow.

The Helper

Use the helper from a provider that cannot reach the Integration Module directly.

Basic Usage

Pass the identifier your integration declares, and a type parameter for typed options. For example:

providers/payment-my/services/my-payment.ts
1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"
2import type { MyOptions } from "../../integration-my/services/my-integration"
3
4// inside any provider method
5const options = await resolveIntegrationOptions<MyOptions>({
6 identifier: "my"
7})

The returned is typed as , already decrypted and validated.

Targeting a Specific Instance

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-my/services/my-payment.ts
1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"
2import type { MyOptions } from "../../integration-my/services/my-integration"
3
4// inside any provider method
5const options = await resolveIntegrationOptions<MyOptions>({
6 identifier: "my",
7 instance_id: this.instanceId_,
8})

works here because the base provider class stores the registration's own instance id at construction; a provider that doesn't support multiple instances can leave out entirely.

When It Isn't Configured

By default, the helper throws a when the integration is not configured, disabled, or incomplete:

1// throws if the integration isn't usable yet
2const options = await resolveIntegrationOptions<MyOptions>({
3 identifier: "my"
4})

Pass to receive instead, so you can handle the missing case yourself:

1const options = await resolveIntegrationOptions<MyOptions>(
2 { identifier: "my" },
3 { optional: true }
4)
5
6if (!options) {
7 // not configured yet: skip, fall back, or return a friendly error
8}

With , a missing, disabled, or incomplete integration resolves to here instead of throwing, which is what the check above is guarding against.

The Service Method

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/my-payment/route.ts
1import { INTEGRATION_MODULE, IntegrationModuleService } from "@gorgo/medusa-integration"
2import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
3
4export async function GET(req: MedusaRequest, res: MedusaResponse) {
5 const integration: IntegrationModuleService = req.scope.resolve(INTEGRATION_MODULE)
6
7 const resolved = await integration.getResolvedOptions("my")
8 if (!resolved) {
9 return res.status(503).send("My is not configured")
10 }
11
12 const options = resolved.options as MyOptions
13 const { provider_id, category, is_enabled } = resolved.meta
14}

returns . A value is , where holds , , and . A result means the integration is not configured, disabled, or incomplete.

Resolving Inside a Workflow

When you compose your own workflow, use the exported step instead of the helper:

workflows/create-my.ts
1import { createWorkflow, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
2import { getResolvedIntegrationOptionsStep } from "@gorgo/medusa-integration"
3
4export const createMyWorkflow = createWorkflow("create-my", () => {
5 const resolved = getResolvedIntegrationOptionsStep({
6 identifier: "my"
7 })
8 return new WorkflowResponse(resolved)
9})

The standalone is also exported. It is what the helper runs internally.

What Resolved Means

Resolved options are not the raw stored values. On every resolve, the module does three things:

  • decrypts secret fields,
  • confirms the integration is enabled and complete, meaning it passes full validation,
  • applies descriptor defaults for options the admin never set.

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.

Caching and Freshness

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.

References

Edited Aug 12, 2026·Edit this page