# GraphQL API Openship exposes Keystone GraphQL at `/api/graphql`. The generated `schema.graphql` includes list operations for shops, channels, orders, items, matches, links and tracking, plus custom product, order, webhook and purchase operations. Do not send production shop/channel credentials or API keys to a public demo playground. Inspect the generated schema locally or use an authorized non-production deployment. Authentication [#authentication] Dashboard sessions and bearer API keys are present in current source. API keys have hashed secret material, scopes, status, expiry and usage fields. Verify that every operation you expose checks the expected scope and row ownership; a stored scope is not enforcement by itself. Example: search a shop catalog [#example-search-a-shop-catalog] The current generated contract accepts `shopId` and `searchEntry` and returns a provider-neutral `ShopProduct`: ```graphql query SearchShopProducts($shopId: ID!, $search: String) { searchShopProducts(shopId: $shopId, searchEntry: $search) { productId variantId title image price inventory inventoryTracked availableForSale productLink error } } ``` Core custom operations [#core-custom-operations] Current generated names include: * `searchShopProducts`, `getShopProduct`, and `searchShopOrders`; * `searchChannelProducts` and `getChannelProduct`; * `getShopWebhooks`, `createShopWebhook`, and `deleteShopWebhook`; * `getChannelWebhooks`, `createChannelWebhook`, and `deleteChannelWebhook`; * `createChannelPurchase`, `cancelPurchase`, and `cancelOrder`; * `getMatch`, `getMatchCount`, `upsertMatch`, `overwriteMatch`, and `matchOrder`. Read exact inputs and result types from `schema.graphql`. Handler support differs by platform. A generated operation can exist while one adapter returns an error or lacks a production implementation. Safety checks [#safety-checks] Before external access, test missing/expired/wrong-scope keys, cross-user IDs, arbitrary endpoint input, provider timeouts, duplicate purchase retries, webhook signature/replay, redacted errors and cancellation after partial fulfillment. # Channels A **Channel** is one user-owned fulfillment destination. A **ChannelPlatform** describes how Openship searches the destination catalog, retrieves one item, creates or cancels a downstream purchase, registers provider webhooks, and interprets tracking or cancellation events. Current source contains compiled channel adapters for Shopify and Openfront only. A supplier, warehouse, 3PL, marketplace, WooCommerce, or BigCommerce connection requires a separately implemented and verified adapter or custom endpoint. A configured ChannelPlatform does not prove that an external purchase, cancellation, or tracking update occurred. Current model boundary [#current-model-boundary] `Channel` belongs directly to one Openship user and references one `ChannelPlatform`. It holds destination identity, domain, access/refresh token fields, expiry, metadata, channel items, links, and cart items. `ChannelPlatform` stores operation selectors and OAuth configuration. `Match` connects a shop item/variant to one channel item/variant. `CartItem` records the selected downstream line, and `TrackingDetail` records tracking facts propagated through the routing path. The upstream shop and downstream provider remain authoritative for their own inventory, orders, funds, and shipment state. Channel ownership is user-scoped. Every operation must also validate ownership of the linked shop, order, match, cart item, and channel item; selecting a related ID must not move data across users. Compiled adapters and custom execution [#compiled-adapters-and-custom-execution] Current files under `features/integrations/channel` provide Shopify and Openfront handlers plus an executor. The operation family includes channel product search/detail, purchase creation/cancellation, webhook management, and tracking interpretation. The executor can also call a database-selected HTTP URL or dynamic function path. Treat that as privileged code/network configuration. Restrict destinations, prevent private-address and redirect escapes, cap time/body size, validate request and response schemas, isolate credentials, and use stable provider idempotency keys. The current configurable path is not a general secure 3PL connector by itself. Routing workflow [#routing-workflow] 1. Configure a synthetic Shop and ShopPlatform. 2. Open `/dashboard/platform/channels` and configure a ChannelPlatform tied to an implemented test adapter. 3. Create a user-owned Channel with sandbox credentials. 4. Use channel product search/detail to identify a candidate variant. 5. Open `/dashboard/platform/matches` and match the shop line to that exact channel item. 6. Link the shop and channel, then route a synthetic order through `matchOrder`, cart commands, and `createChannelPurchase`/`placeOrders` as required by the current schema. 7. Record downstream purchase identity and test tracking/cancellation callbacks. 8. Reconcile Openship, shop, and channel state after success, duplicate delivery, timeout, partial failure, and cancellation. There is no current channel-onboarding mutation or demo seed. The docs site's POST-only demo endpoints return in-memory synthetic payloads for adapter development; they are not a fulfillment provider, durable order store, invoice service, or live 3PL. Controlled GraphQL boundary [#controlled-graphql-boundary] Current custom operations include `searchChannelProducts`, `getChannelProduct`, `createChannelPurchase`, `cancelPurchase`, channel webhook management, matching queries/commands, `addToCart`, `addMatchToCart`, and `placeOrders`. Handler support differs by adapter, so inspect `schema.graphql` and the selected implementation together. Do not use generated CRUD to mark a purchase fulfilled or tracking complete when an external effect has not been authenticated and reconciled. Preserve the provider event ID, route ownership, bounded status/error evidence, and immutable shop/channel item identities. Current limitations [#current-limitations] Current source does not establish WooCommerce, BigCommerce, Amazon, email, spreadsheet, generic 3PL, or carrier integrations. It does not provide payment settlement, warehouse inventory authority, durable retry for every webhook path, or proof that arbitrary configured HTTP endpoints are safe. Channel credential fields also require a deployment-specific encryption and field-access review. One current cancellation handler queries a `CartItem.title` field while the registered model defines `CartItem.name`; verify and repair that route in the owning Openship source before relying on channel cancellation. Also test webhook raw-body signatures, replay IDs, asynchronous failure persistence, token redaction, cross-user IDs, duplicate purchases, cancellation after partial fulfillment, and tracking reconciliation. See [Product matching](/docs/openship/ecommerce/product-matching) and [Create a custom channel integration](/docs/openship/ecommerce/how-to-guides/create-custom-channel). # Comparisons Openship is a self-hosted order-routing application. It stores shops, channels, links, matches, orders, and downstream purchase state, then invokes configured adapter operations to read source orders and place fulfillment purchases. Its main distinction from a hosted order-management service is ownership: you operate the source, database, deployment, credentials, adapter destinations, retries, and reconciliation. Compare operating models [#compare-operating-models] | Approach | Integration boundary | Operations responsibility | Typical tradeoff | | -------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | Openship | Built-in or configured shop/channel adapter operations | The operator owns deployment, credentials, routing behavior, and recovery | Source-level control with corresponding engineering and operational work | | Hosted order-management service | Vendor connectors, rules, webhooks, and APIs | The vendor operates the core service; the customer configures it | Less core infrastructure work, within the vendor's connector and rule model | | Marketplace or store routing app | One platform's extension surface | Shared between the app vendor and merchant | Faster fit for supported platform workflows, with a narrower platform boundary | | Custom routing service | Contracts and workflows designed by the implementing team | Entirely owned by that team | Maximum freedom without Openship's existing models, dashboard, and adapter executors | Compare exact connectors, cancellation and fulfillment behavior, retry semantics, audit evidence, support, and total operating cost—not category labels alone. Current Openship integration scope [#current-openship-integration-scope] Current compiled source includes Shopify and Openfront handlers on both shop and channel sides. Platform records can also configure operation destinations for independent HTTP route apps or local modules, subject to the executor's contract. That architecture is extensible, but it is not the same as verified compatibility with every commerce or fulfillment system. Each additional platform needs implementations for the operations it uses, authentication and credential handling, payload validation, webhook verification, idempotency, timeout and retry policy, and reconciliation tests. Routing behavior [#routing-behavior] Openship can: * record source orders and their line items; * select configured shop-to-channel links using link filters and rank; * use saved item matches to build downstream cart items; * call channel adapters to create purchases; * retain status and error information for operator review. Delivery latency depends on webhook or polling behavior, queueing, provider availability, and the configured execution path. Current source does not establish a universal real-time delivery guarantee. Cost and setup [#cost-and-setup] Open source does not mean zero operating cost or setup in a fixed number of minutes. A useful evaluation needs a database, deployment, operator account, credentials, shop/channel records, links or matches, webhook configuration, and a tested failure-and-reconciliation path. Infrastructure, provider, marketplace, support, monitoring, and implementation charges remain deployment-specific. When Openship may fit [#when-openship-may-fit] Consider Openship when a team: * wants to own and modify its order-routing source and data; * has a supported adapter or is prepared to implement and test one; * needs explicit shop, channel, link, match, and downstream purchase records; * can operate retries, reconciliation, credential rotation, and webhook verification. A hosted or platform-specific alternative may fit better when a required connector, service-level agreement, support program, reporting suite, or compliance program already exists there and source-level customization is not required. # Deployment Openship needs a Node.js runtime and persistent PostgreSQL. Each enabled shop or channel integration adds its own credential, network, webhook and operational requirements. Build and migration warning [#build-and-migration-warning] The current `npm run build` script generates the Keystone schema, deploys Prisma migrations, and builds Next.js. Use one reviewed release job for migrations rather than allowing multiple build workers to race against production. Release checklist [#release-checklist] 1. Pin the source revision and install from the lockfile. 2. Generate the schema and run current static checks. 3. Back up the target and test migrations on a realistic disposable clone. 4. Store `DATABASE_URL`, `SESSION_SECRET` and integration credentials in the host's secret manager. 5. Apply migrations once, deploy the app, and run session/API-key ownership negatives. 6. Route a synthetic order through every enabled shop/channel pair. 7. Test duplicate delivery, timeout, retry, partial failure, cancellation, fulfillment and tracking updates. 8. Verify raw webhook signatures and replay IDs. 9. Monitor stuck orders and keep an operator reconciliation path. Railway, Vercel, Render or other templates can create infrastructure. They do not verify database sizing, migration history, provider reachability, background work, retries, webhooks, backups or incident response. A successful deploy is not an order-routing readiness claim. # Getting started Prerequisites [#prerequisites] * Node.js 20 or newer * PostgreSQL * Git and npm * an empty, isolated database Clone and install [#clone-and-install] ```bash git clone https://github.com/openshiporg/openship.git cd openship npm install ``` Configure the local environment [#configure-the-local-environment] Copy the repository's `.env.example` when present and set at least: ```bash DATABASE_URL="postgresql://username:password@localhost:5432/openship" SESSION_SECRET="replace-with-a-random-string-at-least-32-characters" ``` Add only the SMTP, AI, shop or channel secrets required by the handler you intend to test. Keep credentials out of source control. Review migrations and start [#review-migrations-and-start] ```bash npm run dev ``` The current script generates the Keystone schema, runs `prisma migrate deploy`, and starts Next.js. Confirm the database target and migration history before running it. Create the first dashboard user [#create-the-first-dashboard-user] Open `/dashboard/init`, create the first operator, and sign in at `/dashboard`. Current source has no Openship onboarding mutation or seed, so platform, shop, channel, link, and match records are configured manually. Do not copy credentials from a public example. Build one synthetic route [#build-one-synthetic-route] Create one test shop, one test channel, a link, and one exact variant match. Current compiled handlers cover Shopify and Openfront; the docs site's POST-only demo endpoints can exercise custom-handler shapes with in-memory synthetic data. Route a synthetic order and inspect both Openship and the downstream test system. Evaluation checks [#evaluation-checks] * API keys and sessions cannot read another user's shops, channels, matches or orders. * Shop and Channel credential fields are denied or encrypted before real secrets are stored. * Database-selected HTTP/function destinations are constrained to reviewed allowlists and cannot reach private or metadata networks through DNS or redirects. * retries do not create duplicate downstream purchases. * webhook signatures and event IDs are checked before state changes. * a partial channel failure remains visible and can be retried or reconciled. `npm run build` currently deploys migrations before building, the checked-in `lint` script uses the removed Next.js 16 `next lint` command, and the package has no test or typecheck script. Do not represent a release as fully verified or connect a production shop/supplier until the owning source adds current gates and the exact handler passes product, order, ownership, egress, credential, cancellation, fulfillment, tracking, replay, retry, and reconciliation tests. # Openship order routing [Source](https://github.com/openshiporg/openship) · [Getting started](/docs/openship/ecommerce/getting-started) Openship connects the places where orders originate with the places that fulfill them. A **shop** is an order source. A **channel** is a fulfillment destination. **Links** connect shops to channels, and **matches** map a shop item to a channel item before an order is placed downstream. The repository contains shop-to-channel routing and Shopify/Openfront adapters. A configured shop/channel, provider name, model, or synthetic endpoint response does not prove an external purchase, webhook, retry, cancellation, or tracking flow. Current credential fields, configurable execution destinations, and non-durable callback paths require hardening before production credentials or orders. Architecture and data [#architecture-and-data] Openship uses the same Next.js, Keystone, GraphQL, Prisma and PostgreSQL application base as Openfront. Its active graph is smaller and routing-specific: users/roles/API keys, shop and channel platforms, shops and channels, orders/line/cart items, shop/channel items, matches, links and tracking details. Ownership is directly User-scoped; there is no organization or workspace tenant graph. Provider-specific behavior belongs behind shop and channel platform handlers. Openship stores its routing and match state; connected commerce or fulfillment platforms remain authoritative for their own products, orders and tracking records. Main workflow [#main-workflow] 1. Configure a shop platform and shop with scoped credentials. 2. Configure a channel platform and fulfillment channel. 3. Link the shop to the channel. 4. Search both product catalogs and create a match. 5. Import or select a shop order. 6. Convert matched lines into channel purchases. 7. record tracking, cancellation, fulfillment or error state from the channel boundary. Idempotency, signature verification, credential isolation, retries and reconciliation need to be proved for each adapter. Openfront relationship [#openfront-relationship] Openfront products can be shop sources for Openship, but Openship is not the Openfront storefront or payment system. It coordinates order routing across independently operated systems. # Product matching A `Match` records how one or more source `ShopItem` records map to one or more destination `ChannelItem` records. Openship uses these saved mappings when an order is configured with `matchOrder`. Match records [#match-records] A match has: * `input`: the shop-side product ID, variant ID, quantity, shop, and owning user; * `output`: the channel-side product ID, variant ID, quantity, saved price, channel, and owning user; * an owning user used by access filters and order matching. The relationships are many-to-many. A one-input, one-output match covers a direct variant mapping; one input with several outputs can represent a bundle. Create a match [#create-a-match] Connect a shop and channel [#connect-a-shop-and-channel] Configure credentials and verify that both adapter search operations work. Current compiled handlers cover Shopify and Openfront. Other platforms require compatible configured operations. Search each platform [#search-each-platform] The Matches workspace calls the selected shop and channel adapters to search products on demand. This is not a background import of either complete catalog. Select exact items [#select-exact-items] Choose the source and destination product/variant identities and quantities. Current source does not provide an automatic SKU-matching workflow, so confirm each mapping explicitly. Save and test [#save-and-test] Creating a `Match` reuses or creates the corresponding `ShopItem` and `ChannelItem` records. Route a synthetic order and inspect the generated cart items before enabling downstream purchase creation. How an order uses matches [#how-an-order-uses-matches] When a new order has `matchOrder` enabled and does not take the separate link-routing path, current source: 1. loads the order and its line items; 2. searches matches owned by the same user using product ID, variant ID, and quantity; 3. first looks for a combined match covering all order lines, then tries individual line matches; 4. reads each matched destination product through its channel adapter; 5. creates downstream `CartItem` records from the saved outputs; 6. records a price-change error when the current destination price differs from the saved match price; 7. optionally calls downstream purchase placement when `processOrder` is enabled. If no suitable match is found, Openship records a match error and leaves the order for operator handling rather than proving successful fulfillment. Matching is exact application logic, not probabilistic product identification. Product IDs, variant IDs, quantities, ownership, adapter responses, and current prices must all be tested. Retries and downstream purchase creation also need idempotency and reconciliation coverage before live routing. Current boundaries [#current-boundaries] * There is no automatic full-catalog import in the current Matches workflow. * There is no implemented automatic SKU-matching action. * Availability- or destination-based selection among several channels is not implemented by the Match lookup. * Inventory synchronization is eligible only for one-input/one-output matches where both quantities are `1` and both adapters expose inventory values. * No checked-in benchmark establishes a supported catalog size or routing throughput. * A saved match does not guarantee that a destination item is still available, unchanged, or purchasable; the channel response and resulting errors still require review. # Schema Visualizer Database Schema [#database-schema] Explore the complete Openship database schema with this interactive visualization. Click and drag to navigate, zoom to focus on specific areas, and see how all the models connect together.