openlinker.io · Architecture · Integrations · vs BaseLinker
Sync orders, inventory, and listings between your shop and the marketplaces you sell on. Self-hosted, open-source, pluggable.
If you sell on your own shop and on marketplaces like Allegro, you've already lived the pain: orders coming in from multiple places, inventory drifting between channels, manually rewriting product descriptions for every listing, customer data scattered across systems. The usual answers are SaaS channel managers (you pay forever and your data lives on their servers) or custom scripts (fragile and yours to maintain alone). OpenLinker is the third option: a self-hosted, open-source orchestration platform that does the same job, on your servers, with your data, with code you can read and extend.
Status: alpha, pre-1.0. Expect rough edges; report them.
- Orders flow automatically. Every Allegro order creates a PrestaShop order, with the buyer auto-provisioned as a customer. Cursor-based and resumable — nothing's lost if anything pauses.
- Stock stays in sync, both ways. Sell on Allegro, your shop stock drops. Restock in your shop, Allegro listings update. No per-platform spreadsheet.
- Create listings without copy-pasting. Pick a product, pick a category (or look it up by EAN), get an Allegro offer up with images, parameters, GPSR data, and your saved seller policies — from one wizard. AI can draft the description if you let it.
- Run multiple stores from one dashboard. Each connection has its own encrypted credentials and config. Two PrestaShop stores plus three Allegro accounts? One OpenLinker instance.
- You own everything. Self-hosted, Apache 2.0. No subscription, no per-order pricing, no vendor that can shut you off. You host it; that's the trade.
| Integration | Role | Status |
|---|---|---|
| PrestaShop | Shop (source + destination) | ✅ Live |
| WooCommerce (guide) | Shop (source + destination + inventory) | ✅ Live |
| Allegro (guide) | Marketplace (source + offers + shipping) | ✅ Live |
| Erli (guide) | Marketplace (offers + source) | ✅ Live |
| AI router (Anthropic, OpenAI) | Content suggestion | ✅ Live |
| InPost (guide) | Shipping (ShipX — paczkomat + kurier, labels, webhooks) | ✅ Live |
| DPD (guide) | Shipping (REST labels + protocols, SOAP tracking) | ✅ Live |
| Subiekt nexo (guide · tutorial) | Invoicing (via Sfera bridge — first InvoicingPort adapter) |
✅ Live |
| KSeF (guide · tutorial) | Invoicing (Polish national e-invoicing) | ✅ Live |
| Infakt | Invoicing (accounting SaaS — issues invoices and relays them to KSeF on the seller's behalf) | ✅ Live |
| Shopify · BigCommerce · Magento | Shop | 📋 Planned |
| eBay · Amazon · OLX · Empik · Bol | Marketplace | 📋 Planned |
| DHL · FedEx · ORLEN Paczka · GLS | Shipping (siblings under ShippingProviderManagerPort) |
📋 Planned |
| Fakturownia · iFirma · wFirma | Invoicing (siblings under InvoicingPort) |
📋 Planned |
Planned items are open for community contributions — see Adding your own integration below.
OpenLinker is built around a small set of capability ports. Each integration implements one or more of them — adding a new platform means adding implementations, not changing core code.
| Capability | What it does | Integrations |
|---|---|---|
| Catalog & inventory | Read products, variants, and stock from a master shop | PrestaShop · WooCommerce |
| Orders | Ingest from any source (event journal or watermark); create + manage them in a destination shop | PrestaShop (source + destination) · WooCommerce (source + destination) · Allegro (source) · Erli (source) |
| Offers / listings | Manage marketplace offers — categories, prices, quantities, seller policies, GPSR data | Allegro · Erli |
| Shipping | Generate labels + handover protocols, fetch tracking (via ShippingProviderManagerPort) |
Allegro · InPost · DPD |
| Invoicing | Issue fiscal documents and, where supported, submit them to a tax authority for clearance (via InvoicingPort) |
Subiekt nexo · KSeF · Infakt |
| Content suggestion | Provider-agnostic AI completions with editable, versioned prompts | AI router (Anthropic, OpenAI) |
| Auth & ops | Per-integration connection testers, webhook provisioners, OAuth, retry classifiers, credentials/config validators | All integrations |
See docs/capabilities.md for the full, code-synced vocabulary — every capability port, its base contract, and all 31 sub-capabilities with descriptions and guards — and docs/architecture-overview.md for the contracts plugin authors implement against.
Host-provided, regardless of integration: encrypted credentials store · multi-connection per platform type (two PrestaShop stores from one instance) · identifier mapping with a single unified seed (ol_product_*, ol_order_*, ol_variant_*, …) · customer identity resolution with optional email-fallback · PII-aware storage (full or hash-only) · sync-job orchestration with retry classification + outcome tracking · browser-based admin UI.
Role: shop / destination
OpenLinker's PrestaShop integration. Implements the full shop surface: catalog and inventory reads, order ingestion via the date_upd watermark, order creation with auto-provisioned guest customers, status lifecycle, cancellations, and returns. Ships the OL Dynamic Carrier PrestaShop module so buyer-paid shipping costs from marketplaces round-trip correctly into PrestaShop orders without breaking existing carriers. Registers connection tester, webhook provisioner, and connection-config + credentials shape validators with the host.
~7.6k LOC source · libs/integrations/prestashop/ · README · also the recommended starting point when adding a new shop or marketplace plugin
Role: marketplace / source
OpenLinker's Allegro integration. Implements every offer sub-capability the OfferManager port defines — OfferLister, OfferEventReader, OfferFieldUpdater, CategoryBrowser, CategoryBarcodeMatcher, CategoryParametersReader, CatalogProductReader, OfferCreator, OfferStatusReader, OfferReader, SellerPoliciesReader, ResponsibleProducerReader, SafetyAttachmentUploader. Order ingestion via the Allegro event journal with cursor persistence. Full OAuth flow with shared token state, refresh-on-401 retry, and a plugin-owned migration. Email normalization for Allegro's masked-buyer-email format.
~7.5k LOC source · libs/integrations/allegro/ · also a useful template for any marketplace plugin that needs OAuth
Role: content suggestion
OpenLinker's AI router for content generation. Wraps Anthropic and OpenAI behind a single AiCompletionPort, with per-provider encrypted key storage, admin-switchable active provider, and Anthropic prompt-caching wired in. Drives the offer-description suggestion flow in the create-offer wizard.
~572 LOC source · libs/integrations/ai/ · also a useful template for plugins that route through an external SaaS rather than per-connection
- vs SaaS channel managers (BaseLinker, ChannelEngine, ChannelAdvisor) — you own the data, you own the code, and your bill is your hosting bill. No per-order pricing, no vendor lock-in, no surprise terms changes.
- vs custom scripts — a tested foundation that doesn't break the next time Allegro changes their API. Real integration tests run against a real PrestaShop install via Testcontainers.
- vs headless commerce platforms (Medusa, Vendure, Spree) — OpenLinker is the integration plumbing for the shop you already have, not a replacement shop platform. Keep PrestaShop (or WooCommerce, BigCommerce, …) running; add OpenLinker alongside it.
How OpenLinker measures against the standard set of multichannel-orchestration flows. Honest about gaps — they're either on the roadmap or intentionally out of scope.
Legend: ✅ supported today ·
| Flow | Status | Capability / port |
|---|---|---|
| Orders | ||
| Ingest orders from any source (marketplace or shop) | ✅ | OrderSourcePort |
| Create + manage orders in destination shop | ✅ | OrderProcessorManagerPort |
| Generate invoices / fiscal documents (+ tax-authority clearance) | ✅ | InvoicingPort — Subiekt nexo, KSeF, Infakt |
| Accounting export / GL / valuation | — | Out of scope; integrate accounting separately |
| Inventory & catalog | ||
| Read products, variants, attributes | ✅ | ProductMasterPort |
| Read inventory levels | ✅ | InventoryMasterPort |
| Multi-location / multi-warehouse stock | 🛣️ | InventoryMasterPort accepts locationId; not yet exercised by an adapter |
| Bulk offer creation | ✅ | Bulk offer-creation batch (Allegro) + bulk shop-publish (WooCommerce) |
| Bulk quantity batch-update | OfferQuantityBatchUpdater sub-capability defined; no adapter implements it yet |
|
| Marketplace listings | ||
| Create + update offers (categories, policies, GPSR) | ✅ | OfferManagerPort + sub-capability ports |
| AI offer descriptions | ✅ | AiCompletionPort |
| Repricing / dynamic pricing rules | 🛣️ | PricingAuthorityPort planned |
| Customers | ||
| Identity resolution across channels | ✅ | Customer-identity service + plugin-provided email normalizer |
| GDPR-friendly PII modes (full or hash-only) | ✅ | Host-provided storage policy |
| Customer Q&A / messaging | — | Out of scope |
| Shipping & carriers | ||
| Carrier mapping (marketplace ↔ physical carrier) | ✅ | Host-provided carrier-mapping service |
| Pickup-point handling (Paczkomat, etc.) | ✅ | Through OrderSourcePort order shape |
| Buyer-paid shipping cost round-trip | ✅ | OrderProcessorManagerPort + plugin-owned shop module |
| Generate shipping labels + handover protocols | ✅ | ShippingProviderManagerPort — InPost, DPD, Allegro |
| Fetch tracking + push status back to marketplace | ✅ | ShippingProviderManagerPort tracking + order-status writeback (ADR-027) |
| Payments | ||
| Payment status from order data | ✅ | Part of the IncomingOrder shape from OrderSourcePort |
| Refunds / returns lifecycle | 🛣️ | OL-owned order-status state machine deferred (#1032); the refunded payment status is read-only forward-compat (no source emits it in v1) |
| Payment provider integrations (Stripe, P24, …) | 🛣️ | PaymentProcessorPort planned |
| Operations | ||
| Multiple shops + marketplaces in one instance | ✅ | Host-provided connection model |
| Multi-user with roles | Host-provided JWT + admin role gate; finer-grained roles are a known gap | |
| Encrypted credentials store | ✅ | Host-provided crypto service |
| Webhooks (HMAC + replay + dedup) | ✅ | Host-provided webhook pipeline |
| Sync-job retry + outcome tracking | ✅ | Host-provided sync-job service + plugin-provided retry classifier |
| Workflow automation ("when X, do Y" rules) | — | Out of scope; write a worker handler instead |
| Reporting / analytics dashboard | — | Out of scope; export via the API |
Spot a gap that's on your evaluation checklist? Open a feature request — the table is a living scorecard, not a fixed roadmap.
Marketplace (Allegro) Shop (PrestaShop)
│ ▲
│ 1. order event │ 4. create order,
▼ │ update status
[OrderSourcePort] [OrderProcessorManagerPort]
│ ▲
│ 2. hydrate │ 3. map customer,
│ full order │ resolve products
▼ │
OpenLinker core ─────────────────────────┘
(identifier mapping · retries · dedup · projections)
Every platform — shop or marketplace — implements the same set of typed capability ports (OrderSourcePort, OrderProcessorManagerPort, ProductMasterPort, InventoryMasterPort, OfferManagerPort). Core orchestration is platform-agnostic; per-platform behaviour lives in self-registering plugin packages. Full picture in Architecture Overview.
OpenLinker is built so that adding a platform is a plugin, not a fork.
A new integration is a workspace package under libs/integrations/<name>/. The path from idea to merged PR:
- Scaffold.
pnpm create-adapter <your-platform>generates the package skeleton —package.json,tsconfig, a stub plugin descriptor, and the canonical hexagonal layout. - Pick your roles. Shop, marketplace, content suggestion, or something new entirely? That decides which capability ports you'll implement. Implement only the ports your platform supports; capability guards handle the rest at runtime.
- Implement. Each port has 1–10 methods. PrestaShop is the multi-port reference (~7.6k LOC across catalog, inventory, orders). Allegro is the OAuth + marketplace reference (~7.5k LOC, every offer sub-capability). The AI router is the thin single-port example (572 LOC). Effort scales with how much of a platform you cover.
- Register. Add your plugin to
apps/api/src/plugins.ts(andapps/web/src/plugins/index.tsfor any FE contributions — routes, nav, wizards, error renderers). - Test. Use
@openlinker/test-kitfor integration tests against real Postgres + Redis + a real shop install via Testcontainers. Use the in-memory fakes from each context's/testingsubpath for unit tests.
Full walkthrough in the Plugin Author Guide. The PrestaShop reference adapter README is the closest thing to a worked example.
-
Capabilities are open-world.
Capability,EntityType, andPlatformTypeare open strings at the registry boundary. AddShippingProvider,PricingAuthority, or anything else without a core PR — the well-known set is a hint, not a gate. -
Framework-free domain.
libs/core/src/**/domain/has zero NestJS / TypeORM imports. Runtime-enforced:@openlinker/coreexposes only 28 explicit subpaths viapackage.json#exports, ESLint blocks deep imports in plugin packages, andpackage exportsblock the runtime path entirely. -
Framework-neutral plugin SDK.
@openlinker/plugin-sdkships theAdapterPlugincontract, theHostServicesbag, and the typeddispatchCapability<T>helper. Plugins compile against the SDK, not against the host's NestJS / TypeORM. -
Frontend is pluggable too. Routes, nav items, the typed
ApiClient, offer-creation wizards, structured error renderers — all extension points populated from a per-platform plugin folder underapps/web/src/plugins/. Allegro and PrestaShop are the in-tree examples. -
Real tests, exported. Integration tests run against real Postgres + Redis + a real PrestaShop install via Testcontainers. The harness ships as
@openlinker/test-kit. Each context exports in-memory fakes from a/testingsub-barrel for plugin unit tests.
Any 📋 Planned row in the Integrations table is open for community contribution — or propose something not listed via the new_integration issue template. The Plugin Author Guide walks through port selection, package layout, registry wiring, OAuth, and tests.
OpenLinker is moving fast and publicly. See recent activity for what's landed lately, the modularity audit (#546) for the architectural thread making the codebase plugin-ready, and the OSS launch epic (#670) for what's blocking the first public release.
Want to click through OpenLinker without setting up a dev environment? The demo overlay boots the entire stack in Docker — PostgreSQL, Redis, MySQL, a seeded PrestaShop, and the full OpenLinker app tier (API + Worker + admin UI) — from a clean checkout:
For a full step-by-step walkthrough (example
.env, PrestaShop + Allegro connection wiring, end-to-end verification, and troubleshooting) see the One-Command Demo Setup Guide.
git clone https://github.com/openlinker-project/openlinker.git
cd openlinker
# Required pre-step: provide the credentials encryption key (not committable).
# .env.example already ships an empty OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=
# line, so drop it and append the generated one instead of `>> .env`, which
# would leave two lines with the same key (harmless — env-file parsing is
# last-wins — but confusing to anyone editing .env by hand).
grep -v '^OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=' .env.example > .env
echo "OPENLINKER_CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
pnpm demo:up # builds the images and starts everything (first run takes a while)
pnpm demo:logs # follow the logs
pnpm demo:down # stop the stack (add -v to also wipe the data volumes)Required:
OPENLINKER_CREDENTIALS_ENCRYPTION_KEYmust be set (via the root.env) beforepnpm demo:up. The credentials-encryption migration fails closed without it and aborts the boot. Generate one withopenssl rand -base64 32. See.env.example.
Database migrations run automatically (one-shot migrate container) before the API and Worker start — no manual migration:run needed.
| Service | URL | Credentials |
|---|---|---|
| OpenLinker admin UI | http://localhost:8090 | admin / admin |
| OpenLinker API | http://localhost:3000 | — |
| PrestaShop storefront | http://localhost:8080 | — |
| PrestaShop admin | http://localhost:8080/admin-dev | demo@prestashop.com / prestashop_demo |
phpMyAdmin (PrestaShop MySQL browser, http://localhost:8081, root / root) is a devtools-only auxiliary service — it's gated behind a Compose devtools profile rather than always-on. Opt in with pnpm dev:stack:devtools:up (plain dev stack) or pnpm demo:devtools:up (demo overlay).
PrestaShop auto-installs with a seeded catalog and the OpenLinker module pre-mounted. The PrestaShop ↔ OpenLinker connection itself is configured manually in the admin UI (Connections → New) — see the Operator Guide.
Configuring the PrestaShop connection (container networking): the API and Worker containers reach PrestaShop over the Compose network by its service name, not localhost. When creating the connection, set both the Shop URL and the Storefront URL to http://prestashop (not http://localhost:8080) — localhost from inside a container resolves to the container itself. The demo pre-configures PrestaShop for this (a prestashop-domain shop URL plus disabled canonical redirect) via a post-install step, so webservice calls and server-side product-image downloads (OpenLinker fetches image bytes itself, then re-uploads them to the marketplace CDN) resolve correctly.
Known limitation (follow-up): with the Storefront URL set to
http://prestashop, product-image download works from the app tier, but the operator's browser can't resolveprestashop, so product thumbnails in the OpenLinker UI won't render. Decoupling the server-side download base from the browser display base is a tracked follow-up — for the demo, prefer a working image sync over rendered thumbnails.
Notes:
⚠️ Shared volumes: the demo shares the same Compose project (openlinker) and data volumes aspnpm dev:stack:up. On a machine with an existing local dev stack, running the demo reuses and can clobber that data — the two flows are not isolated. Stop and, if needed, wipe one before running the other (pnpm dev:stack:down/pnpm demo:down -v). Requires Docker Compose ≥ 2.24.VITE_API_BASE_URLis baked into the UI bundle at image build time (defaulthttp://localhost:3000); rebuild thewebimage to change it.- Demo credentials are intentionally not production-safe — this stack is for local evaluation only.
git clone https://github.com/openlinker-project/openlinker.git
cd openlinker
pnpm install
cp apps/api/.env.example apps/api/.env.local
cp apps/worker/.env.example apps/worker/.env.local
# apps/web/.env.example ships safe defaults; copy only if you need to override VITE_API_BASE_URL
pnpm dev:stack:up # PostgreSQL · Redis · MySQL · PrestaShop · WooCommerce in Docker
pnpm --filter @openlinker/api migration:run # Create database tables
pnpm start:dev:api # NestJS API on :3000
pnpm start:dev:worker # Background job worker
pnpm start:dev:web # React admin UI on :4173Then follow docs/getting-started.md — a walkthrough from a clean machine to a working PrestaShop + Allegro setup with categories mapped.
- Node.js 22+ (LTS), pnpm 10+
- Docker + Docker Compose (dev stack + integration tests)
- An Allegro sandbox account if you want to exercise the marketplace path
The exact versions the Docker images and Compose files are built/tested against:
| Component | Version |
|---|---|
| Node.js | 22 (pinned node:22.23.1-alpine3.24 in Dockerfile / apps/web/Dockerfile) |
| pnpm | 10 (pinned pnpm@10.33.4, matching package.json's packageManager field) |
| Docker Compose | ≥ 2.24 (required by the demo overlay's !reset/!override merge keys) |
| PostgreSQL | 17 (postgres:17-alpine) |
| Redis | 8.4 (redis:8.4-alpine) |
| MySQL | 8.4.7 (mysql:8.4.7-oraclelinux9) |
The dev stack starts PostgreSQL, Redis, MySQL, PrestaShop, and WooCommerce in containers — you do not need any of those installed locally.
⚠️ Upgrading an existing checkout (one-time, #1411).Two changes combine into a one-time data reset of your local dev/demo stack:
- Every named volume was renamed
_data→-data(e.g.postgres_data→postgres-data). Docker qualifies volumes as<project>_<name>, so your existingopenlinker_postgres_datais orphaned and Compose creates an emptyopenlinker-postgres-data.- PostgreSQL moved 16 → 17. PG17 refuses to start against a PG16 data directory (
database files are incompatible with server), so preserving the old volume by hand would not work either.The next
pnpm dev:stack:up/pnpm demo:uptherefore starts with an empty database, silently — your configured connections, encrypted credentials, seeded PrestaShop catalog, and WooCommerce setup are still on disk in the orphaned volumes, just invisible. Re-seed from scratch and reclaim the disk:docker compose down docker volume rm \ openlinker_postgres_data openlinker_redis_data openlinker_mysql_data \ openlinker_prestashop_data openlinker_woocommerce_mysql_data \ openlinker_woocommerce_data openlinker_caddy_data openlinker_caddy_config pnpm dev:stack:up # re-seeds from scratchAdjust the
openlinker_prefix if you setCOMPOSE_PROJECT_NAME. This affects local dev/demo data only — there is no production deployment to migrate.
WooCommerce is available at http://localhost:8082 (PrestaShop uses 8080). Run pnpm dev:stack:wc-credentials after startup to retrieve the auto-generated consumer key and secret. See the WooCommerce Setup Guide for full configuration steps.
pnpm lint # ESLint
pnpm type-check # TypeScript strict mode, all workspaces
pnpm test # Unit tests (no Docker needed)
pnpm test:integration # Integration tests via Testcontainers (needs Docker)
pnpm format # Prettier
pnpm build # All workspacesQuality gate before every commit: pnpm lint && pnpm type-check && pnpm test.
For schema changes, also run pnpm --filter @openlinker/api migration:show to confirm there are no missing migrations. See docs/migrations.md.
- Operator Guide — UI walkthrough for self-hosters (setup + day-to-day usage)
- Architecture Overview — bounded contexts, capability ports, data flow
- Engineering Standards — naming, file layout, repository-port pattern
- Frontend Architecture — admin UI conventions, state rules
- Testing Guide — Testcontainers + harness usage
- Plugin Author Guide — adding a new integration
- Connections & Adapter Resolution — per-connection runtime model
- Public API Contract — what's stable for plugin authors and downstream consumers; versioning policy.
Pull requests welcome. See CONTRIBUTING.md for the workflow, branch naming, and the Closes #N PR convention.
If you're adding a new platform, jump to Adding your own integration above for the five-step path.
- Code of Conduct — the standards we hold ourselves and contributors to
- Support — where to ask questions vs. file bugs vs. propose features
- Security — responsible-disclosure process (do not open public issues for vulnerabilities)
Apache License 2.0. See LICENSE.

