Docs
July 31, 2026
Product

Announcing the Integration Module for Medusa

A new module for Medusa: plugins simply declare their settings, and admins manage them in the Admin, with no medusa-config edits and no redeploys.

Announcing the Integration Module for Medusa

At Gorgo, we've been building Medusa plugins for over a year, plugins that connect stores to payment providers, fulfillment services, and ERP systems. The same question kept coming up in every one of them: how do you pass a plugin its parameters, and how do you manage them? API keys, connection settings, and other user configuration are usually set through and environment variables, so any change requires a code edit and a redeploy. To avoid that, every plugin has to build its own UI, wire up an API, validate fields, and write workflows.

Today we're announcing the Integration Module for Medusa, which takes this chore away. Plugins simply declare their parameters in the module's provider, and the store admin manages them in the Admin through a generated interface, with no edits and no redeploys.

The problem with plugin settings

Configuring a Medusa plugin usually comes down to two problems, and both are harder than they should be.

  • Managed through and env vars: Any change (a new API key, switching from test to live) needs code access and a full redeploy.
  • Building the UI is hard: Proper settings management means forms and modals, field validation, API routes, and workflows, all written and maintained separately for every plugin.

What the Integration Module does

The Integration Module moves plugin settings out of code and into the Admin and the database. A plugin describes the settings it needs, a store admin fills them in under Settings → Integrations, and at runtime the plugin reads ready, validated values.

As an author, you only describe the settings schema; the module takes care of the UI, storage, encryption, and validation. And if a plugin needs something the generated UI doesn't support, you can create a custom widget.

How it works

You declare a descriptor with that lists your options and their types, the sections they group into, and the validation rules. From that single declaration, the Admin generates the settings page: the right control for each field, validation, conditional visibility, and masked secrets. You don't have to write any UI, CRUD API, or the associated workflows.

A minimal provider is a descriptor plus the class that ships it:

src/providers/integration-acme/services/acme.ts
1import { AbstractIntegrationProvider, defineIntegration } from "@gorgo/medusa-integration"
2
3const descriptor = defineIntegration({
4 category: "payment",
5 displayName: "acme.name",
6 supportsMultipleInstances: true,
7
8 options: {
9 apiKey: {
10 type: "string",
11 required: true,
12 secret: true,
13 label: "acme.fields.apiKey",
14 },
15 mode: {
16 type: "enum",
17 values: ["test", "live"],
18 default: "test",
19 label: "acme.fields.mode",
20 },
21 },
22
23 sections: [
24 {
25 id: "credentials",
26 title: "acme.sections.credentials",
27 options: ["apiKey", "mode"]
28 },
29 ],
30
31 testConnection: async ({ options }) => {
32 const ok = await pingAcme(options.apiKey, options.mode)
33 return ok ? { status: "passed" } : { status: "failed", message: "Invalid API key" }
34 },
35})
36
37export class AcmeIntegrationProvider extends AbstractIntegrationProvider {
38 static identifier = "acme"
39 get descriptor() {
40 return descriptor
41 }
42}

Values like are i18n keys, resolved through Medusa's standard localization, and the translations themselves live in .

Next, register the provider in :

medusa-config.ts
1const ACME_INTEGRATION_ID = "acme-1"
2
3module.exports = defineConfig({
4 plugins: [
5 {
6 resolve: "@acme/medusa-payment-acme",
7 options: {},
8 },
9 {
10 resolve: "@gorgo/medusa-integration",
11 options: {
12 encryptionKey: process.env.INTEGRATION_ENCRYPTION_KEY,
13 providers: [
14 {
15 resolve: "@acme/medusa-payment-acme/providers/integration-acme",
16 id: ACME_INTEGRATION_ID,
17 },
18 ],
19 },
20 },
21 ],
22 modules: [
23 {
24 resolve: "@medusajs/medusa/payment",
25 options: {
26 providers: [
27 {
28 resolve: "@acme/medusa-payment-acme/providers/payment-acme",
29 id: "acme",
30 options: {
31 id: ACME_INTEGRATION_ID // matches the integration provider id above
32 },
33 },
34 ],
35 },
36 },
37 ],
38})

Here sets the integration instance key. The same has to be passed to the consumer, a payment provider in this example. That's what the module uses to find and return the settings of the right integration. The two s are declared in independent places in the config, so you have to keep them in sync by hand, and it's easier to do that through a constant.

(here ) is the AES-256-GCM key the module uses to encrypt secrets at rest. Set it in your environment and keep it stable, since changing or losing it makes stored secrets unreadable.

At runtime, the plugin reads its parameters anywhere it runs with and gets back a typed, decrypted object. Incomplete or disabled settings don't resolve, so a half-filled draft is never returned.

src/providers/payment-acme/services/acme.ts
1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"
2
3abstract class AcmeBase extends AbstractPaymentProvider {
4 constructor(options) {
5 super(options)
6 const { apiKey, mode } = await resolveIntegrationOptions({
7 identifier: "acme",
8 instance_id: options.id
9 })
10 }
11
12 // ...
13}

What you get

One descriptor gives your plugin a complete settings interface:

  1. Settings management under Settings → Integrations, with no edits and no redeploys.
  2. Flexible option typing, conditional visibility, required fields, and validation rules.
  3. Secrets stored encrypted (AES-256-GCM), never sent to the browser.
  4. Support for multiple settings instances, for stores that have several accounts across third-party services.
  5. A "Test connection" button for verifying the connection.
  6. Custom widgets for the UI, when you need something beyond the standard form.
  7. A typed, already-decrypted config that resolves on demand.

For plugin authors

If you already maintain a Medusa plugin, moving to the Integration Module comes down to a simple port: move your existing options into the module provider's descriptor. And you read them with where you currently read environment variables or . We migrated all of our own providers this way (payments, fulfillment, and ERP), and you can browse a complete Medusa app with every provider wired up in the repository.

The step-by-step guide walks you through the whole migration.

Discover integrations in the Admin

The module also displays a catalog of available integrations right in the Admin. Store admins can find your plugin and install it with a short guide. And because the settings UI is generated, your plugin looks clean and native, in the same style as the Medusa Admin.

Discover integrations in the Admin

Getting started

Install the module:

npm install @gorgo/medusa-integration

From there, declare your plugin's settings and other metadata, register it as an Integration Module provider, and switch it to read its parameters with . The full installation and usage guide is in the documentation, and the source is on GitHub.

What's next

The Integration Module is in beta, and we're actively developing it, adding built-in integrations and expanding the catalog. If you're building a Medusa plugin, we'll help you move over. If you run a Medusa store, give it a try and tell us what's missing.

Join the discussion in the Medusa Telegram community. Issues and Pull Requests are always welcome in the repository on GitHub.