A new Medusa module that lets plugins declare their settings so store admins can configure them right in the Admin – no medusa-config edits and redeploys.

At Gorgo, we've spent the past year building Medusa plugins that connect stores to third-party services such as payment providers, ERPs, and fulfillment carriers. Across every one of them, the same friction kept surfacing: getting an integration configured. Credentials, modes, and webhooks lived in and environment variables, so every change meant editing code and shipping a redeploy, and each plugin had to build its own settings screen from scratch.
Today we're announcing the Integration Module – a Medusa module that removes both chores. A plugin declares its settings once, and store admins configure them from a generated form in the Admin, with no edits, no redeploys, and secrets encrypted at rest.
Configuring a Medusa plugin usually comes down to two chores, and both are more painful than they should be.
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 in a form under Settings → Integrations, and at runtime the plugin reads a ready, validated, decrypted config.
As an author, you only describe the form. The module handles the UI, storage, encryption, and validation for you. And when a plugin needs something the generated form can't express, you can drop in a custom widget.
You declare a descriptor with that lists your options and their types, the sections they group into, and any validation rules. From that single declaration, the Admin generates the settings form: the right control for each field, validation, conditional visibility, and masked secrets. There's no UI to build.
A minimal provider is a descriptor plus the class that ships it:
1import { AbstractIntegrationProvider, defineIntegration } from "@gorgo/medusa-integration"2
3const descriptor = defineIntegration({4 category: "payment",5 displayName: "acme.name",6 supportsMultipleInstances: true,7 options: {8 apiKey: {9 type: "string",10 required: true,11 secret: true,12 label: "acme.fields.apiKey",13 },14 mode: {15 type: "enum",16 values: ["test", "live"],17 default: "test",18 label: "acme.fields.mode",19 },20 },21 sections: [22 {23 id: "credentials",24 title: "acme.sections.credentials",25 options: ["apiKey", "mode"]26 },27 ],28 testConnection: async ({ options }) => {29 const ok = await pingAcme(options.apiKey, options.mode)30 return ok ? { status: "passed" } : { status: "failed", message: "Invalid API key" }31 },32})33
34export class AcmeIntegrationProvider extends AbstractIntegrationProvider {35 static identifier = "acme"36 get descriptor() {37 return descriptor38 }39}Labels like are i18n keys, resolved from your plugin's translations.
Register the provider in : the integration module stores and encrypts its settings, and the consuming module (here, payments) reads them by matching .
1module.exports = defineConfig({2 plugins: [3 {4 resolve: "@acme/medusa-payment-acme",5 options: {},6 },7 {8 resolve: "@gorgo/medusa-integration",9 options: {10 encryptionKey: process.env.INTEGRATION_ENCRYPTION_KEY,11 providers: [12 {13 resolve: "@acme/medusa-payment-acme/providers/integration-acme",14 id: "acme-1",15 },16 ],17 },18 },19 ],20 modules: [21 {22 resolve: "@medusajs/medusa/payment",23 options: {24 providers: [25 {26 resolve: "@acme/medusa-payment-acme/providers/payment-acme",27 id: "acme",28 options: { id: "acme-1" }, // matches the integration provider id above29 },30 ],31 },32 },33 ],34})At runtime, your plugin reads its configuration anywhere it runs with , and gets back a typed, decrypted object. Incomplete or disabled configurations never resolve, so a half-filled draft can't leak into production.
1import { resolveIntegrationOptions } from "@gorgo/medusa-integration"2
3const { apiKey, mode } = await resolveIntegrationOptions({ identifier: "acme" })One descriptor gives your plugin a complete configuration experience:
If you already maintain a Medusa plugin, adopting the module is mostly mechanical: move your existing options into a descriptor, and read them with where you currently read env vars. We migrated all of our own providers this way (payments, ERP, and fulfillment), and you can browse the complete setup in the all-integrations example.
The step-by-step guide walks through it end to end.
The module also ships a Browse catalog inside the Admin. Store admins discover available integrations and add them without touching code. Because the settings UI is generated, your plugin shows up with a clean, native interface, consistent with the rest of the dashboard.
Install the module:
npm install @gorgo/medusa-integrationFrom there, register your providers and declare their settings. The full installation and usage guide is in the documentation, and the source is on GitHub.
The Integration Module is in beta, and we're actively expanding it: more built-in integrations and a growing catalog. If you maintain a Medusa plugin, we'd love to help you migrate it. If you run a Medusa store, give it a try and tell us what's missing. Join the conversation in our Telegram community. Issues and Pull Requests are always welcome on GitHub.