Quickstart
This page walks you through building a minimal but real ShopiMind integration, from an empty project all the way to the manifest you submit for registration. The path is deliberately as short as possible: you install the kit and the SDK, you write a skeleton integration, you run it locally, then you generate its manifest.
You write a defineIntegration({...}); the JavaScript integration kit (@shopimind/integration-kit-js) does the rest: webhook server, signature verification, sync scheduler, a local store (a SQLite database, inside your service) and a ready-to-use, authenticated ShopiMind HTTP client on ctx.spm.
The kit depends on and re-exports the SDK (@shopimind/sdk-js): you import SDK resources and types from the kit, and you call the SDK's static methods on ctx.spm to push your data.
Before you start
- Node.js 18+ (both packages are ESM-only, Node ≥ 18.17).
- Some TypeScript knowledge. All the code on this page is in TypeScript.
- No ShopiMind credentials are required for this first walkthrough: you run the integration locally and generate its manifest offline.
1. Create the project
Create a folder, initialize package.json, then add TypeScript:
mkdir my-app && cd my-app
yarn init -y
yarn add -D typescript @types/nodeSince the kit and the SDK are ESM-only, your project must be too. In package.json, declare "type": "module" and a build script:
{
"name": "my-app",
"type": "module",
"scripts": {
"build": "tsc -b",
"start": "node dist/main.js",
"print:manifest": "node dist/print-manifest.js"
}
}A minimal tsconfig.json that emits modern ESM to dist/:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": "src",
"strict": true
},
"include": ["src"]
}2. Install the kit, the SDK and dotenv
yarn add @shopimind/integration-kit-js @shopimind/sdk-js dotenv| Package | Role |
|---|---|
@shopimind/integration-kit-js | The integration runtime (^1.6.0): webhook server, HMAC signature, scheduler, SQLite store, lifecycle dispatch. It depends on and re-exports the SDK. |
@shopimind/sdk-js | The outbound HTTP client to the ShopiMind API (spm-api-key header). The kit builds the client for you and exposes it on ctx.spm; you call the SDK's static methods on it. |
dotenv | Loads your .env file into process.env at startup. |
Import the SDK from the kit
Because the kit re-exports the SDK (export * from '@shopimind/sdk-js'), import SDK resources and types — SpmCustomers, SpmEvents, SpmHelpers, … — directly from @shopimind/integration-kit-js. You never instantiate the SDK yourself.
3. Write the integration (src/integration.ts)
The integration is the heart of your project. You declare it with defineIntegration; the kit validates its shape at startup (valid slug, no duplicate sync entity…).
Start by typing your settings in src/settings.ts:
// src/settings.ts
import type { RawConfigs } from '@shopimind/integration-kit-js';
export interface MySettings {
apiKey: string;
}
export function parseSettings(raw: RawConfigs): MySettings {
return { apiKey: String(raw.api_key ?? '') };
}Then the integration itself. Here is a minimal skeleton — each optional field (provisioning, widgets, remoteData, hooks) will be detailed in the following pages:
// src/integration.ts
import { defineIntegration } from '@shopimind/integration-kit-js';
import type { Integration, IntegrationContext } from '@shopimind/integration-kit-js';
import type { MySettings } from './settings.js';
import { parseSettings } from './settings.js';
export const myIntegration: Integration<MySettings> = defineIntegration({
slug: 'my-app', // ^[a-z0-9_-]+$
meta: {
name: 'My App',
version: '1.0.0',
categories: ['crm'], // neutral slugs, resolved at registration
},
configSchema: {
steps: [
{
key: 'connection',
label: { fr: 'Connexion', en: 'Connection' },
fields: [
{
key: 'api_key',
type: 'password',
required: true,
sensitive: true,
label: { fr: 'Clé API', en: 'API key' },
},
],
on_complete: { action: 'test_connection' },
},
],
},
parseSettings,
testConnection: async (ctx: IntegrationContext<MySettings>) => Boolean(ctx.settings.apiKey),
syncSteps: [], // your sync steps — see data-sync
});Keep in mind
installationId(inctx) is an opaque token issued by ShopiMind: never interpret it.- You push your data through
ctx.spm— a raw, already-authenticatedSpmHttpClient. You call the SDK's static methods on it, for exampleSpmCustomers.bulkSave(ctx.spm, items, { chunk: true })orSpmEvents.trigger(ctx.spm, codeName, payload). In a sync step, prefer the safe push helperctx.sendBulk(...)— see Sync your data.
4. Wire up the runtime (src/main.ts)
The entry point maps your environment variables to the kit options, then starts the server. The kit builds the ShopiMind HTTP client itself — you do not instantiate the SDK and there is no gateway to wire up.
// src/main.ts
import 'dotenv/config';
import { createIntegrationApp } from '@shopimind/integration-kit-js';
import { myIntegration } from './integration.js';
const env = process.env;
const required = (key: string): string => {
const v = env[key];
if (!v) throw new Error(`Required environment variable: ${key}`);
return v;
};
const app = createIntegrationApp(myIntegration, {
databasePath: env.DATABASE_PATH ?? './data/store.sqlite',
webhookSecret: required('WEBHOOK_SECRET'),
credentialsKey: env.CREDENTIALS_KEY ?? undefined,
allowPlaintextSecrets: env.NODE_ENV !== 'production', // dev only; prod requires CREDENTIALS_KEY
adminToken: env.ADMIN_TOKEN ?? undefined,
backfillDays: env.BACKFILL_DAYS ? Number(env.BACKFILL_DAYS) : 365,
syncIntervalMinutes: env.SYNC_INTERVAL_MINUTES ? Number(env.SYNC_INTERVAL_MINUTES) : 15,
port: env.PORT ? Number(env.PORT) : 8080,
...(env.SHOPIMIND_API_URL ? { spmBaseUrl: env.SHOPIMIND_API_URL } : {}),
});
void app.start().catch((e: unknown) => {
console.error('Integration startup failed', e);
process.exit(1);
});
process.on('SIGTERM', () => void app.stop().then(() => process.exit(0)));
process.on('SIGINT', () => void app.stop().then(() => process.exit(0)));createIntegrationApp builds the authenticated SpmHttpClient and exposes it on ctx.spm. The default ShopiMind API base is https://core.shopimind.com; pass spmBaseUrl (here from SHOPIMIND_API_URL) to override it.
ctx.spm is the raw SDK client
ctx.spm is a plain SpmHttpClient; you drive it with the SDK's static methods, all re-exported by the kit. The SDK never throws on an HTTP error — it returns an envelope { ok, statusCode, data, error }. Use SpmHelpers.unwrapOrThrow(env) or SpmHelpers.extractCounts(env) to handle it.
Environment variables (.env)
Create a .env file at the root of the integration:
WEBHOOK_SECRET= # HMAC, provided by ShopiMind at registration (required)
CREDENTIALS_KEY= # AES-256 (64 hex) — encryption of secrets at rest; REQUIRED in production
ADMIN_TOKEN= # protects the /admin/* routes
PORT=8080
SYNC_INTERVAL_MINUTES=15
BACKFILL_DAYS=365
SHOPIMIND_API_URL= # overrides the API base (default https://core.shopimind.com)
DATABASE_PATH=./data/store.sqliteFor this first run
WEBHOOK_SECRET is required: without it, the integration won't start. You'll receive it at registration (see step 6). To start locally right now, put any non-empty value in it; webhooks actually signed by ShopiMind will only be accepted with the real secret. In production, CREDENTIALS_KEY (64 hex) becomes required — otherwise the integration refuses to start (fail-closed). Set allowPlaintextSecrets: true only for local dev.
5. Run locally
Build, then start:
yarn build
yarn startThe server listens on port 8080 by default. Check that it's alive via the /health liveness probe (no authentication):
curl http://localhost:8080/health
# → {"status":"ok"}The kit exposes several HTTP routes. You have nothing to code on the server side: it serves them for you.
| Method | Path | Role |
|---|---|---|
POST | /webhook/receive | All lifecycle events (HMAC-signed) |
POST | /webhook/test-connection | Connection test from the ShopiMind UI (HMAC-signed) |
POST | /webhook/remote-data/{resource} | Dynamic options of a select (HMAC-signed) |
GET | /health | Liveness probe |
Verification of the incoming webhooks' HMAC X-Shopimind-Signature signature is done automatically by the kit. You have no signature validation to write.
6. Generate the manifest
The manifest is a neutral JSON describing your integration (slug, config, widgets, relative webhooks, lifecycle events). It's what you submit to ShopiMind for registration — it contains no SQL, no secret, no absolute URL.
Add a small src/print-manifest.ts script:
// src/print-manifest.ts
import { buildIntegrationManifest } from '@shopimind/integration-kit-js';
import { myIntegration } from './integration.js';
console.log(JSON.stringify(buildIntegrationManifest(myIntegration), null, 2));The print:manifest script (declared in step 1) compiles and runs it. Redirect the output to a file:
yarn build
yarn print:manifest > integration.manifest.jsonSend this file to dev@shopimind.com for registration. ShopiMind then generates the webhook_secret (to place in WEBHOOK_SECRET), resolves your categories and finalizes the registration.
Your integration is running
You have an integration that starts, responds on /health, exposes its signed webhooks and produces its manifest. It doesn't push any data yet: that's the subject of the following steps.
What's next
- → The kit in detail — the full
defineIntegrationcontract, theIntegrationContext, thecreateIntegrationAppoptions and the store. - → Configuration — steps, fields, dynamic options (
remote),sensitiveandtest_connection. - → Sync your data — declare
SyncSteps, manage the cursor and push safely viactx.sendBulkand the SDK statics onctx.spm. - → JavaScript SDK — the outbound HTTP client reference.