Skip to content

Primer for AI Agents

This page is written for AI coding assistants and for developers who want the whole mental model on one screen. It condenses the rest of this documentation into one page; each section links to the authoritative one. A machine-readable index of the full documentation is at /llms.txt.

If the task is about… You are in Start at
A native iOS/Android shopping app, its UI, its checkout, an extension or a theme Shopping Apps (Shopgate CONNECT) Shopping Apps introduction
Inventory, order routing, ship-from-store, click & collect, customer or catalog data over REST Omnichannel Suite Omnichannel welcome

The two products have separate APIs, separate auth and separate concepts. Pipelines belong to Shopping Apps. REST services under *.shopgate.io belong to the Omnichannel Suite. Do not mix them.


A Shopgate app is a native shell around a React/Redux frontend (called Engage) that talks to a Node.js backend (called CONNECT). You do not fork the app. You extend it with extensions: npm-like packages that contribute steps and pipelines to the backend and portals, subscribers, reducers, widgets and translations to the frontend. Merchant-specific behaviour lives in extensions; the theme stays upgradeable.

Reference: App Architecture


Requires Node.js 18+ and Git. Reference: Set up your developer environment

Terminal window
npm install -g @shopgate/platform-sdk
Terminal window
sgconnect login
Terminal window
sgconnect init

sgconnect init creates the application folder — the workspace that holds extensions/ and themes/. All further commands run from there.

Command Purpose
sgconnect extension create [types...] Scaffold a new extension (backend, frontend or both)
sgconnect extension attach [name...] Attach extensions to the sandbox app (no name = all)
sgconnect extension detach [name...] Detach again
sgconnect extension manage Interactive list of extensions and their attach status
sgconnect backend start Run the backend runtime locally
sgconnect frontend setup One-time frontend environment setup
sgconnect frontend start Run the frontend dev server
sgconnect extension upload [dir] Upload to the Developer Center
sgconnect theme upload Upload a theme

Attach/detach changes only take effect after restarting the backend and/or frontend process.

Reference: Platform SDK reference


extensions/myFirstExtension/
├── extension/ # Backend: step files (plain Node.js), node_modules
├── pipelines/ # Backend: pipeline definitions (.json)
├── frontend/ # Frontend: React components, subscribers, reducers, locale files
└── extension-config.json # Manifest: id, version, trust level, config, components, steps

Extension IDs follow @<organization>/<name>, e.g. @myAwesomeOrganization/myFirstExtension. The organization part is assigned to you when you create your developer account.

Reference: Hello World part 1

Minimal:

{
"id": "@myAwesomeOrganization/myFirstExtension",
"version": "1.0.0",
"trusted": false
}

Full set of top-level properties:

Property Type Required Purpose
id string yes Extension id, @org/name
version string yes semver
trusted boolean no Runs in the trusted environment (payments, login)
configuration object no Values injected into frontend and/or backend
components array no Frontend contributions
steps array no Backend steps that hook into existing pipelines

Reference: Extension config reference


Every step file lives under extension/ and exports one function:

module.exports = async (context, input) => {
const message = 'Hello World'
return { message }
}
  • context — meta information, storage access, logger, app and device info.
  • input — the input the pipeline hands to this step.
  • The returned object is the step’s output. Property names must match the output keys declared for the step.

Reference: Step context reference

A pipeline is a JSON file that chains steps

Section titled “A pipeline is a JSON file that chains steps”

The file name must match the id, and the id must start with your organization id. Everything after that is up to you: myAwesomeOrganization.myFirstPipeline is valid. Shopgate’s own pipelines use a longer four-part convention — <organization>.<collection>.<action>.<version>, as in shopgate.catalog.getProducts.v1 — and new pipelines should follow it. Both forms appear in this documentation: the pipelines guide states the four-part convention, the Hello World tutorial uses the short form.

{
"version": "1",
"pipeline": {
"id": "myAwesomeOrganization.myFirstPipeline",
"public": true,
"input": [],
"output": [
{ "key": "message", "id": "1" }
],
"steps": [
{
"type": "extension",
"id": "@myAwesomeOrganization/myFirstExtension",
"path": "@myAwesomeOrganization/myFirstExtension/helloWorld.js",
"input": [],
"output": [
{ "key": "message", "id": "1" }
]
}
]
}
}
  • public: false means the pipeline can only be called by another pipeline in the same environment, not from the app.
  • Steps must consume all declared pipeline input and produce all declared output, otherwise the request fails.
  • A pipeline request times out after 20 seconds and returns ETIMEOUT. Anything slower needs to be async or cached.

Reference: Pipelines guide · Pipeline reference

Extending Shopgate’s pipelines instead of writing your own

Section titled “Extending Shopgate’s pipelines instead of writing your own”

To add behaviour to an existing pipeline, declare a step with a hook string in extension-config.json:

"steps": [
{
"path": "extension/addBonusPoints.js",
"description": "Adds bonus points to each product; place after price calculation.",
"hooks": ["shopgate.catalog.getProducts.v1:after"],
"input": [{ "key": "productId" }],
"output": [{ "key": "bonusPoints", "addPipelineOutput": true, "optional": true }]
}
]

Hook string format is {pipelineName}:{hookName}. Use *:{hookName} to hook into every pipeline that exposes that hook. Steps are only inserted into hooks of pipelines with the same trust level.

Every pipeline implicitly has a before and an after hookbefore steps see and can modify the pipeline input, after steps see input and output and can modify the output. Writing a hook step literally named before or after into a pipeline file is an error. Custom hook steps are declared explicitly in the pipeline:

{
"type": "hook",
"id": "getProductImageUrl",
"input": [{ "key": "productId", "id": "1" }],
"output": [{ "key": "imageUrl", "id": "10" }]
}

One hook can hold steps from several extensions, and their execution order is not guaranteed — never write a hook step that depends on another hook step having run.

Two different path formats — a common source of errors. In extension-config.jsonsteps[].path, the path is relative to the extension root and must start with extension/ (e.g. extension/myStep.js); a path outside the extension throws an error. In a pipeline filesteps[].path, the path is the fully qualified @org/extension/file.js form (e.g. @myAwesomeOrganization/myFirstExtension/helloWorld.js).

Reference: Step insertion · Hook step

Trusted pipelines and steps may use steps that regular ones cannot, and are meant for sensitive work: payments, user login. Trusted extensions are manually reviewed by Shopgate for malicious code. Only mark an extension trusted if it genuinely needs it.

Terminal window
sgconnect extension attach
Terminal window
sgconnect backend start

Then call the local proxy:

Terminal window
curl -X POST http://localhost:8090/pipelines/myAwesomeOrganization.myFirstPipeline \
-H 'Content-Type: application/json' \
-d '{}'

Portals are named extension points inside the theme. A parent portal wraps or replaces a component; a sibling portal injects a component above or below one. Names follow <feature>.<content>.<position>, e.g. PRODUCT-ITEM.NAME (parent) and PRODUCT-ITEM.NAME.AFTER (sibling).

Register a component in extension-config.json:

"components": [
{
"id": "MyComponent",
"path": "frontend/MyComponent/index.jsx",
"target": "product-item.name.before",
"type": "portals"
}
]

Component types are portals, subscribers, reducers, widgets and translations. target only applies to portals.

Use the app.routes portal. It passes a View component as a prop:

import React from 'react';
import { Route } from '@shopgate/engage/components';
const MyPageComponent = ({ View }) => (
<View>
<div>Hello, I&apos;m a page.</div>
</View>
);
const MyRoute = props => (
<Route path="/myroutepath" component={MyPageComponent} {...props} />
);
export default MyRoute;

Reference: Portals guide · Portals reference · Custom routes

Need Page
Call a pipeline from the frontend Data fetching
Read app state Selectors
React to app events Streams
Dispatch behaviour Actions
Translations Translation system
Tracking / analytics extension Tracking events
Camera, push, geolocation, keychain … Native modules

https://{serviceName}.shopgate.io/v{version}/merchants/{merchantCode}/{entity}

Examples: https://catalog.shopgate.io/v1/merchants/TEST/products, https://order.shopgate.io/v1/merchants/TEST/salesOrders.

Services: catalog, customer, order, import, webhook, location, auth.

Reference: Service URLs

Auth — every request needs a bearer token

Section titled “Auth — every request needs a bearer token”

The generated code samples in the API reference omit this. They will return 401 as-is.

Get a token (OAuth2, x-www-form-urlencoded):

Terminal window
curl -X POST https://auth.shopgate.io/oauth/token \
-H "Authorization: Basic $(printf '%s:%s' "$CLIENT_ID" "$CLIENT_SECRET" | base64)" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode "username=$API_USER_EMAIL" \
--data-urlencode "password=$API_USER_PASSWORD" \
--data-urlencode 'tenantType=merchant' \
--data-urlencode "tenantId=$MERCHANT_CODE"

Response:

{
"access_token": "eyJhbGciOiJIUz...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "kcLKlSPYc2fDr4..."
}

Then use it on every call:

Terminal window
curl "https://catalog.shopgate.io/v1/merchants/$MERCHANT_CODE/products?limit=100" \
-H "Authorization: Bearer $ACCESS_TOKEN"
  • Access token: valid 1 hour.
  • Refresh token: valid 3 months; use grant_type=refresh_token to get a new access token.
  • Staging host: https://auth.demo.shopgatedev.io/oauth/token.
  • clientId / clientSecret come from Shopgate support.

Reference: Authentication

  • Batch creation: entity creation goes through batch routes. Batch routes
  • Sorting, filtering, sparse fields: consistent query parameters across services. Sorting and filtering
  • Everything is scoped by merchantCode.

  1. Never emit an Omnichannel request without an Authorization: Bearer header.
  2. Never fork a theme to change the UI — use a portal, unless the task is an explicitly new theme.
  3. Keep pipeline work under 20 seconds. Long-running work must be moved out.
  4. Don’t mark an extension trusted unless it handles payment or login; it triggers a manual Shopgate review.
  5. Pipeline id and file name must agree, and mind the two path formats: extension/myStep.js in extension-config.json, @org/extension/myStep.js in a pipeline file.
  6. Declared input/output keys and the object the step returns must match exactly.
  7. Never rely on the execution order of steps inside the same hook.
  8. Frontend imports come from @shopgate/engage/*, not from the theme’s internals.
  9. After changing extension-config.json, restart backend and/or frontend — the SDK does not hot-reload the manifest.

Themes, CMS widgets, the consent manager, accessibility, web checkout handover, the cart-integration (legacy) route, and every REST endpoint in detail. Use llms.txt for the full page index.