Plugin API
Console-side GraphQL for installing, configuring, validating, and uninstalling StoreOS plugins.
Where these operations live
Plugin operations are on core-api, the console API — not the storefront API.
| Endpoint | https://core-api.storeos.dev/graphql |
| Auth | Authenticated merchant session |
| Tenant | x-tenant: <tenant-uid> header |
Every operation is scoped to the tenant in that header. There is no cross-tenant plugin call.
Storefront apps do not use this API. If you are building a storefront and need the GA4 ID or a chat widget's app ID, read Reading plugins from your storefront instead.
Configure a plugin
plugin__configure writes credentials for one plugin. The input is a discriminated bag: set pluginId, then fill the one field matching that plugin.
mutation ConfigureSteadfast($input: ConfigurePluginInput!) {
plugin__configure(input: $input)
}{
"input": {
"pluginId": "steadfast",
"steadfast": {
"api_key": "…",
"secret_key": "…",
"webhook_token": "…"
}
}
}Returns Boolean. Behavior worth knowing:
- The write is a
$seton that plugin's path only. Configuring Pathao cannot touch Steadfast, and a partial payload cannot wipe a sibling plugin. pluginIdmust be one of the 20INSTALLABLE_PLUGIN_IDS. Anything else is rejected by validation before reaching the service.- Per-plugin format rules run server-side. A value that fails its pattern is reported as a missing configuration payload, not stored. The exact patterns are in the catalog reference.
google-analyticsandgoogle-tag-managerare rejected with "Use the Google OAuth plugin mutations for this integration".
The input field name is not always the plugin ID — it is camel-cased:
pluginId | Input field |
|---|---|
steadfast | steadfast |
pathao | pathao |
bkash | bkash |
sslcommerz | sslcommerz |
waland | waland |
bulksmsbd | bulksmsbd |
smtp | smtp |
google-search-console | googleSearchConsole |
popupsmart | popupsmart |
hotjar | hotjar |
meta-pixel | metaPixel |
microsoft-clarity | microsoftClarity |
lucky-orange | luckyOrange |
sharechat-pixel | shareChatPixel |
facebook-domain-verification | facebookDomainVerification |
intercom | intercom |
tawk-to | tawkTo |
whatsapp-chat | whatsappChat |
Payments take a mode
{
"input": {
"pluginId": "bkash",
"bkash": {
"sandbox": { "appKey": "…", "appSecret": "…", "username": "…", "password": "…", "secretKey": "…" },
"live": { "appKey": "…", "appSecret": "…", "username": "…", "password": "…", "secretKey": "…" },
"activeMode": "sandbox"
}
}
}Flipping a store live is a re-configure with activeMode: "live" — the sandbox set stays where it is.
Uninstall a plugin
mutation Uninstall($input: UninstallPluginInput!) {
plugin__uninstall(input: $input)
}{ "input": { "pluginId": "hotjar" } }$unsets that one path. The credentials are gone — this is not a soft disable, and there is no undo. Re-enabling means configuring again.
Validate a courier
Checks the stored credentials against the courier's API. Use it right after configuring, so the merchant learns about a bad key immediately rather than on their first real parcel.
mutation Validate($input: ValidateCourierInput!) {
plugin__validateCourier(input: $input)
}{ "input": { "provider": "pathao" } }provider is pathao or steadfast. Returns true, or throws BadRequestException carrying the provider's own error message.
Check payment configuration
query PaymentStatus($provider: PaymentProviderName!) {
plugin__paymentProviderConfigurationStatus(provider: $provider) {
provider
configured
missingFields
}
}provider is bkash or sslcommerz. missingFields lists required keys that are empty on the active credential set — so a store in sandbox mode is judged on its sandbox keys. When no credentials exist at all, every required field is returned.
Send a test notification
mutation TestNotification($input: TestNotificationInput!) {
plugin__testNotificationDeliveries(input: $input) {
channel
provider
recipient
recipientName
configured
idempotencyKey
status
}
}{
"input": {
"channel": "SMS",
"recipientName": "Test User",
"recipientPhoneNumber": "+8801711000000",
"message": "Test from StoreOS"
}
}channel is WHATSAPP, SMS, or EMAIL — resolved to the configured provider for that channel (waland, bulksmsbd, smtp). configured tells you whether credentials were present; idempotencyKey identifies the queued delivery.
Webhook URLs
query WebhookUrls {
plugin__webhookUrls {
provider
kind
url
}
}Returns the four public endpoints for this tenant. kind is webhook or callback. Full detail in Webhooks & callbacks.
Google OAuth plugins
Google Analytics, Google Tag Manager, and Google Search Console connect through OAuth. The merchant grants access once, then picks a container or property from their own account — no IDs are typed by hand, and StoreOS holds the tokens, never your storefront.
1 — Check connection
query GoogleStatus {
plugin__googleOAuthStatus { oauthConfigured connected email }
}oauthConfigured is about the platform (are Google credentials set up at all); connected is about this tenant.
2 — Start the flow
query AuthorizeUrl($input: GoogleOAuthAuthorizeInput!) {
plugin__googleOAuthAuthorizeUrl(input: $input) { url }
}purpose is gtm, ga, or gsc. returnPath is where the merchant lands afterwards. Redirect the browser to url.
3 — List and select
| Purpose | List | Select | Create |
|---|---|---|---|
| GTM | plugin__googleGtmAccounts, plugin__googleGtmContainers | plugin__selectGtmContainer | plugin__createGtmContainer |
| GA4 | plugin__googleGaAccounts, plugin__googleGaMeasurementIds | plugin__selectGaMeasurement | plugin__createGaMeasurement, plugin__provisionGaAccount |
| Search Console | — | plugin__selectSearchConsoleMeta | plugin__verifyGoogleSearchConsole |
Selecting is what actually writes plugins.analytics.gtm / plugins.analytics.ga — the OAuth grant alone changes nothing on the storefront.
4 — Disconnect
| Mutation | Effect |
|---|---|
plugin__clearGtmContainer | Drops the selected GTM container |
plugin__clearGaMeasurement | Drops the selected GA4 measurement |
plugin__clearSearchConsole | Drops the Search Console verification |
plugin__disconnectGoogleOAuth | Revokes the whole Google connection for the tenant |
Merchants who only want to stop one tag should use the specific clear… mutation — plugin__disconnectGoogleOAuth drops everything Google at once.