StoreOSv0.15.0
Plugins

Catalog reference

Every StoreOS plugin — ID, provider key, credential fields, and validation rules.

The catalog package

The plugin catalog is static data, published as @storeos/plugins and imported by both the console and storeos.dev. It is the single source of truth for names, logos, install copy, and which plugins are installable.

import {
  PLUGIN_CATEGORIES,
  INSTALLABLE_PLUGIN_IDS,
  listAllPlugins,
  listInstallablePlugins,
  getPluginById,
  getPluginBySlug,
  getPluginCategory,
  pluginSlug,
  pluginLogoUrl,
  catalogCopyAsParagraphs,
} from "@storeos/plugins";

listAllPlugins().length;          // 23
listInstallablePlugins().length;  // 20
ExportReturns
PLUGIN_CATEGORIESPluginCategory[] — the full tree, in display order
INSTALLABLE_PLUGIN_IDSThe 20 IDs the API accepts for configure/uninstall
listAllPlugins()Flat PluginCatalogItem[] across all categories
listInstallablePlugins()Items with a provider and no comingSoon
getPluginById(id)One item, or null
getPluginBySlug(slug)Matches slug or id, or null
getPluginCategory(item)The category containing that item
pluginSlug(item)item.slug ?? item.id — the /plugins/{slug} segment
pluginLogoUrl(item, base?)Logo path, optionally prefixed with a CDN origin
catalogCopyAsParagraphs(value)Normalizes string | string[] | undefined to string[]

PluginCatalogItem

type PluginCatalogItem = {
  id: PluginId;
  slug?: string;              // URL segment; defaults to id
  name: string;
  description: string;
  tagline?: string;
  installationGuide?: string | string[];
  faq?: string | string[];    // alternating question, answer
  authorName?: string;
  authorUrl?: string;
  logo: string;               // public path, leading slash
  comingSoon?: boolean;       // preview only; Install disabled
  provider?: PluginProvider;  // backend key when connectable today
};

An item is installable when it has a provider and is not comingSoon. comingSoon items still render a detail page so merchants can preview what is landing.


Delivery

PluginIDProviderStatus
PathaopathaopathaoInstallable
SteadfaststeadfaststeadfastInstallable
RedXredx—Coming soon

Pathao — plugins.courier.pathao

FieldTypeNotes
store_idnumberPathao merchant store ID
client_idstringFrom the Pathao merchant dashboard
client_secretstring
usernamestringMerchant account login
passwordstring
webhook_secretstringPaste the StoreOS webhook URL into Pathao

Steadfast — plugins.courier.steadfast

FieldTypeNotes
api_keystringSteadfast merchant panel
secret_keystring
webhook_tokenstringRequired for delivery status sync

Both couriers can be checked live with plugin__validateCourier, and both need a webhook URL pasted into the provider's dashboard.


Payments

PluginIDProviderStatus
bKashbkashbkashInstallable
SSLCOMMERZsslcommerzsslcommerzInstallable

Payment plugins are the only ones with two credential sets plus a mode:

{
  live?:    { /* credentials */ },
  sandbox?: { /* credentials */ },
  activeMode?: "live" | "sandbox",
}

activeMode decides which set is used at checkout, so a merchant can keep sandbox keys in place after going live.

bKash credentials — appKey, appSecret, username, password, secretKey SSLCOMMERZ credentials — storeId, storePassword

Configuration completeness is reported by plugin__paymentProviderConfigurationStatus, which returns configured plus the list of missingFields from the active credential set.


Notifications

PluginIDProviderChannel
WalandwalandwalandWhatsApp
BulkSMSBDbulksmsbdbulksmsbdSMS
SMTPsmtpsmtpEmail
PluginFields
walandapiKey, sessionId
bulksmsbdapiKey, senderId
smtphost, port, username, password, senderFrom

Delivery can be exercised end to end with plugin__testNotificationDeliveries on channel WHATSAPP, SMS, or EMAIL.


Analytics

PluginIDPathFieldValidation
Google Tag Managergoogle-tag-managerplugins.analytics.gtmOAuthSee Google OAuth
Google Analyticsgoogle-analyticsplugins.analytics.gaOAuthSee Google OAuth
Hotjarhotjarplugins.analytics.hotjarsiteId5–12 digits
Meta Pixelmeta-pixelplugins.analytics.metaPixelpixelId5–20 digits
Microsoft Claritymicrosoft-clarityplugins.analytics.clarityprojectId8–16 alphanumerics
Lucky Orangelucky-orangeplugins.analytics.luckyOrangesiteId6–40 of a-z0-9-
ShareChat Pixelsharechat-pixelplugins.analytics.shareChatPixelpixelId4–64 chars, no < or >
Facebook domain verificationfacebook-domain-verificationplugins.seo.facebookDomainVerificationcontentMeta token, see below
TikTok Pixeltiktok-pixel——Coming soon

Validation runs server-side in plugin__configure. A value that fails its pattern is rejected as a missing payload rather than silently stored — so a typo'd pixel ID fails loudly at configure time, not weeks later in your analytics.


Marketing

PluginIDPathFieldValidation
Popupsmartpopupsmartplugins.marketing.popupsmartaccountId3–12 digits
Google Search Consolegoogle-search-consoleplugins.seo.searchConsolemetaContentMeta token, see below
SEO catalogseo-catalog——Coming soon

Meta verification tokens

Google Search Console and Facebook domain verification both accept either the bare token or the full tag pasted from the provider:

<meta name="google-site-verification" content="AbC123_token-value" />

The content="…" value is extracted, then required to be 8–200 characters of A-Za-z0-9_-. Anything containing < or > after extraction is rejected.


Support

PluginIDPathFieldsValidation
Intercomintercomplugins.support.intercomappId6–20 alphanumerics
Tawk.totawk-toplugins.support.tawkTopropertyId, widgetIdProperty: 16–32 hex. Widget: 1–20 alphanumerics
WhatsApp chatwhatsapp-chatplugins.support.whatsappChatphoneNumber, prefilledMessage?Non-digits stripped, then 8–15 digits

whatsapp-chat normalizes before storing: +880 1711-000000 is saved as 8801711000000. An empty prefilledMessage is stored as null, not an empty string.


Rendering the catalog yourself

The catalog is plain data, so a marketplace page is a map over it:

import { PLUGIN_CATEGORIES, pluginSlug, pluginLogoUrl } from "@storeos/plugins";

export function Marketplace({ cdn }: { cdn?: string }) {
  return PLUGIN_CATEGORIES.map((category) => (
    <section key={category.id}>
      <h2>{category.title}</h2>
      <p>{category.description}</p>

      {category.items.map((item) => (
        <a key={item.id} href={`/plugins/${pluginSlug(item)}`}>
          <img src={pluginLogoUrl(item, cdn)} alt="" />
          <h3>{item.name}</h3>
          <p>{item.tagline ?? item.description}</p>
          {item.comingSoon ? <span>Coming soon</span> : null}
        </a>
      ))}
    </section>
  ));
}

installationGuide and faq are authored as string[]. FAQ entries alternate question, answer, question, answer — pair them up before rendering:

import { catalogCopyAsParagraphs, getPluginById } from "@storeos/plugins";

const plugin = getPluginById("popupsmart");
const faq = catalogCopyAsParagraphs(plugin?.faq);

const pairs = faq.reduce<Array<[string, string]>>((acc, line, i) => {
  if (i % 2 === 0) acc.push([line, faq[i + 1] ?? ""]);
  return acc;
}, []);

On this page