Skip to content

Capability & Sub-capability Reference

The canonical, code-synced inventory of every capability port, its base contract, and its composable sub-capabilities. This is the developer-facing counterpart to the integration showcase in the README; for the why of the capability-ports + sub-capabilities design see ADR-002 and the Architecture Overview.

How to read this: core depends on capability ports (business roles). The base port carries only the method every adapter of that role must implement; optional behaviour is split into sub-capabilities — independent interfaces in domain/ports/capabilities/, each with a co-located is{Capability}(adapter) type-guard. Call sites narrow with the guard before invoking, and degrade gracefully when an adapter doesn’t implement it.

Source of truth. Every name below is generated from libs/core/src/**/domain/ports/**. When you add a port or sub-capability, update this file in the same PR. If a table here disagrees with the code, the code wins — fix the doc.


PortContextRegistry capabilityWhat it doesBase method(s)
ProductMasterPortproductsProductMasterSource of truth for the product catalog — read/write products, variants, and categories.getProduct · getProducts · createProduct · updateProduct · deleteProduct · getProductVariants · upsertProductVariant · getProductCategories · assignCategories · searchProducts · listExternalIds
InventoryMasterPortinventoryInventoryMasterSource of truth for stock levels; carries a deprecated reservation surface (reserveInventory / releaseInventory — ADR-061, deprecated in place, removal deferred to a contract-major cycle).getInventory · listInventory · adjustInventory · reserveInventory · releaseInventory · getAvailableQuantity
OrderSourcePortordersOrderSourceCursor-based, read-only ingestion of orders from any source (marketplace journal or shop watermark).listOrderFeed · getOrder
OrderProcessorManagerPortordersOrderProcessorManagerCreate orders on a destination shop.createOrder
OfferManagerPortlistingsOfferManagerManage marketplace offers/listings; base contract is the inventory-driven quantity update.updateOfferQuantity
ShopProductManagerPortlistingsProductPublisherPublish a product as a native listing on a shop (the shop-publish flow), and declare the description grammar the shop accepts (ADR-046).publishProduct · getDescriptionFormat
ShippingProviderManagerPortshippingShippingProviderManager (open-world — see note)Generate shipping labels, read tracking, and list supported shipping methods.generateLabel · getTracking · getSupportedMethods
InvoicingPortinvoicingInvoicingIssue fiscal documents, fetch them, upsert a customer, and report supported document types.issueInvoice · getInvoice · upsertCustomer · getSupportedDocumentTypes
FiscalizationPortfiscalizationFiscalizationHand a completed sale to a provider that performs or brokers its fiscal registration (never OL itself).registerTransaction
(no port yet — Wave 3a)fulfillment-authorityReturnsAuthorityDecide the disposition of returned goods — A5 of the ADR-052 authority matrix. Resolved from a connection’s capability declarations plus its Connection.config claim, not by narrowing a dispatched adapter, so it carries no *Port yet.—
ContentPublisherPortcontent(internal — not registry-resolved)Publish a content field (e.g. a product description) to a channel or the master.publish

Open-world capability vocabulary (#576). The closed, well-known set is CoreCapabilityValues, reproduced in full in the fenced table below — one list, not a second restatement that can drift from it. Fiscalization (#1908) and ReturnsAuthority (#2351) join the closed set rather than the open-world escape because the connection DTOs validate enabledCapabilities against it with a strict @IsIn, so a name kept out of the array is a name an operator cannot write. Adapters may register additional capability strings at runtime without a core change — ShippingProviderManager is the live example (declared in the InPost / DPD / Allegro manifests, resolved through the registry, but intentionally not a member of the closed set). The runtime gate validates a connection’s request against the adapter’s supportedCapabilities, not against the closed union.

CapabilityWhat it does
ProductMasterSource of truth for the product catalog.
InventoryMasterSource of truth for stock levels.
OrderProcessorManagerCreate orders on a destination shop.
OrderSourceCursor-based, read-only ingestion of orders.
OfferManagerManage marketplace offers and listings.
ProductPublisherPublish a product as a native shop listing.
CategoryProvisionerMirror or create a destination category tree.
InvoicingIssue fiscal documents through a provider.
FiscalizationHand a completed sale to a provider that registers it fiscally.
ReturnsAuthorityDecide the disposition of goods a customer sends back (A5, ADR-052).
AvailabilityAuthorityDecide how much of each product is sellable (A1, ADR-052).
FulfillmentExecutorHold a piece of fulfilment work and act on it (A3, ADR-052).

Which integration implements what is each adapter’s own declaration: its supportedCapabilities manifest (libs/integrations/<p>/src/<p>-plugin.ts) is the source of truth, and the capability-level showcase across all integrations lives in the root README. This file stays the platform-neutral vocabulary — the ports and sub-capabilities themselves.


Each is an independent interface + co-located is{Capability} guard. Adapters declare what they support via implements <BasePort>, <SubCapability>, ….

Sub-capabilityWhat it doesMethod(s)Guard
OfferListerPage through the seller’s existing offers.listOffersisOfferLister
OfferReaderFetch a single offer by external id.getOfferisOfferReader
OfferEventReaderRead the marketplace’s incremental offer-event journal.listOfferEventsisOfferEventReader
OfferCreatorCreate a new offer / listing.createOfferisOfferCreator
OfferFieldUpdaterPartially update offer fields (e.g. description text). Optionally declares the description grammar the marketplace accepts (ADR-046) - optional because a marketplace adapter may ship no field-update capability at all, so a destination that declares nothing resolves to a conservative shared subset.updateOfferFields, getDescriptionFormat?isOfferFieldUpdater
OfferStatusReaderRead an offer’s live publication status.getOfferStatusisOfferStatusReader
OfferStockRestorerRestore offer stock after a cancellation.restoreStockOnCancellationisOfferStockRestorer
OfferQuantityBatchUpdaterBulk-update quantities for many offers in one call.updateOfferQuantitiesBatchisOfferQuantityBatchUpdater
PendingQuantityAckReconcilerReconcile outstanding asynchronously-acknowledged quantity writes against a destination’s authoritative status (#2621) - a destination that acknowledges a quantity write synchronously simply doesn’t implement this.reconcilePendingQuantityAcksisPendingQuantityAckReconciler
OfferSmartClassificationReaderFetch the marketplace’s smart/auto classification for an offer.getOfferSmartClassificationisOfferSmartClassificationReader
CategoryBrowserBrowse the marketplace category tree.fetchCategoriesisCategoryBrowser
CategoryBarcodeMatcherAuto-detect a category from a product barcode.matchCategoryByBarcodeisCategoryBarcodeMatcher
CategoryParametersReaderRead the parameter schema a category requires.fetchCategoryParametersisCategoryParametersReader
EanCategoryMatcherResolve categories for a batch of products by EAN.resolveCategoriesForBatchByEanisEanCategoryMatcher
EanCategoryMatcherStreamingResolve categories for a batch of products by EAN, yielding each variant’s outcome as it lands instead of one map at the end. Incremental sibling of EanCategoryMatcher; an adapter that ships only the batch method keeps working.streamCategoriesForBatchByEanisEanCategoryMatcherStreaming
CatalogProductReaderLook up marketplace catalog products by barcode / id.findProductsByBarcode · getProductisCatalogProductReader
SellerPoliciesReaderSurface the seller’s saved return / shipping / warranty policies.fetchSellerPoliciesisSellerPoliciesReader
ResponsibleProducerReaderList GPSR responsible-producer entries.fetchResponsibleProducersisResponsibleProducerReader
SafetyAttachmentUploaderUpload a GPSR safety attachment (manual, label, …).uploadSafetyAttachmentisSafetyAttachmentUploader
TaxonomyBorrowerReuse another platform’s resolved taxonomy for a destination.getBorrowedTaxonomy, allowsBorrowedCatalogueLookup (optional)isTaxonomyBorrower
OfferCurrencyDeclarerDeclare the connection’s real settlement currency, a pure/synchronous adapter-declared value (#3203, mirroring getDescriptionFormat) consumed by DestinationCurrencyResolutionService to answer the price-change currency-mismatch guard. The declared value is a FALLBACK — the operator-set Connection.config.currency key wins when set (#3159 review, BLOCKING), so a wrong fixed assumption (e.g. a non-PL Allegro storefront) always has a remedy.getDestinationCurrencyisOfferCurrencyDeclarer

Adapter coverage: Allegro implements every sub-capability except OfferQuantityBatchUpdater, including EanCategoryMatcherStreaming (#2208, epic #2205); an adapter that ships only EanCategoryMatcher keeps working, because CategoryResolutionService falls back to the batch method (see the README Implementations section); Erli implements a reconciliation-first subset (ADR-025), plus OfferCurrencyDeclarer (both Allegro and Erli declare 'PLN', matching the PL-first / PLN-only assumptions already baked into each adapter).

Sub-capabilityWhat it doesMethod(s)Guard
CategoryProvisionerCreate / ensure a category exists on the destination shop before publishing. Also a registry-level capability (CategoryProvisioner ∈ CoreCapabilityValues).provisionCategoryisCategoryProvisioner
ShopCategoryBrowserBrowse the shop’s existing category tree (drill-down by parent) so an operator can pick a placement — the shop-side sibling of the marketplace CategoryBrowser. Every node is selectable (no leaf gate). Advertised-without-dispatch (not in CoreCapabilityValues): declared in the manifest for discovery, resolved by narrowing the ProductPublisher adapter.browseCategoriesisShopCategoryBrowser
ShopAttributeReaderRead the shop’s store-wide global product attributes + their predefined terms so an operator can pick a structured attribute (linked on publish as pa_* + term ids), with free-text custom attributes as the fallback. Advertised-without-dispatch (not in CoreCapabilityValues): declared in the manifest for discovery, resolved by narrowing the ProductPublisher adapter.listAttributes, listAttributeTermsisShopAttributeReader
ShopProductStatusReaderRead a previously-published product’s live shop-side publication status for the steady-state ShopStatusSyncService reconcile (#1845) — the shop-side sibling of OfferStatusReader. Optional getShopVariationStatus reads a grouped/multi-variant publish’s CHILD variation status scoped under its parent (a variation lives at a different shop-native resource than a standalone simple product). Advertised-without-dispatch (not in CoreCapabilityValues): declared in the manifest for discovery, resolved by narrowing the ProductPublisher adapter.getShopProductStatus, getShopVariationStatus?isShopProductStatusReader
ShopCurrencyDeclarerDeclare the shop’s configured currency, the shop-side sibling of OfferCurrencyDeclarer (#3203). Kept optional rather than folded onto the base port, and ASYNC (Promise<string | null>) unlike its marketplace sibling — a shop’s real currency typically needs a live read (e.g. a store-settings call), so no in-tree shop adapter implements it yet.getDestinationCurrencyisShopCurrencyDeclarer

Adapter coverage: WooCommerce implements CategoryProvisioner (write), ShopCategoryBrowser (read, #1834), ShopAttributeReader (read, #1835), and ShopProductStatusReader (read, #1845, including the grouped-variation-aware read) on its ProductPublisher adapter. Shoper implements CategoryProvisioner (write, #3713), ShopCategoryBrowser, ShopAttributeReader and ShopProductStatusReader (read, #3806) on its ProductPublisher adapter. No shop adapter implements ShopCurrencyDeclarer yet — a WooCommerce destination still falls back to Connection.config.currency (or an operator-set value) for the price-change currency-mismatch guard.

OrderProcessorManagerPort / OrderSourcePort (orders) — 7

Section titled “OrderProcessorManagerPort / OrderSourcePort (orders) — 7”
Sub-capabilityWhat it doesMethod(s)Guard
OrderFulfillmentUpdaterPush a post-create status + tracking update to a destination order.updateFulfillmentisOrderFulfillmentUpdater
OrderStatusWritebackRelay an order-status change back to the originating marketplace (event-as-data).writeisOrderStatusWriteback
FulfillmentStatusReaderRead a destination order’s current fulfillment status.getFulfillmentStatusisFulfillmentStatusReader
DestinationOptionsReaderList a destination’s carriers / order-statuses / payment-methods for mapping.listCarriers · listOrderStatuses · listPaymentMethodsisDestinationOptionsReader
SourceOptionsReaderList a source’s order-statuses / delivery-methods / payment-methods for mapping.listOrderStatuses · listDeliveryMethods · listPaymentMethodsisSourceOptionsReader
OrderCreateStockEffectDeclare that creating an order on this destination lowered the shop’s own stock, so core may consume the order’s holds on positions that destination masters. Probed after the create; anything but a declared true keeps the holds until dispatch. Shoper answers from its shopping_update_stock_on_buy setting; PrestaShop from the state validateOrder created the order in (not PS_STOCK_MANAGEMENT, which does not gate the decrement), refusing packs and legacy depends_on_stock rows; WooCommerce from the _reduced_stock item meta on the created order’s lines (stock drops only for a paid-like status on a stock-managed product). PrestaShop and WooCommerce answer for the order the same adapter instance just created, so a skipped create answers false.lowersStockOnOrderCreateisOrderCreateStockEffect
OrderContactRefresherReconcile an order the destination already holds with contact details the source revealed only later. Called best-effort on the create-skip path (the order is already mapped), never after a fresh create; a throw is warn-logged and does not change the sync result. Shoper replaces the placeholder phone it had to create the order with, but only while the shop order still carries it.refreshOrderContactisOrderContactRefresher

The canonical OL-owned order-lifecycle state machine (authoritative updateOrderStatus / cancelOrder / processReturn / getOrders) is deferred — see #1032.

ShippingProviderManagerPort (shipping) — 4

Section titled “ShippingProviderManagerPort (shipping) — 4”
Sub-capabilityWhat it doesMethod(s)Guard
LabelDocumentReaderFetch the label document for an already-registered shipment.fetchLabelisLabelDocumentReader
DispatchProtocolReaderGenerate a handover / dispatch protocol for a batch of shipments.generateProtocolisDispatchProtocolReader
PickupPointFinderSearch the carrier’s pickup points / lockers (e.g. Paczkomat).findPickupPointsisPickupPointFinder
ShipmentCancellerCancel / void a registered shipment.cancelShipmentisShipmentCanceller
Sub-capabilityWhat it doesMethod(s)Guard
RegulatoryStatusReaderRead the clearance status of a previously-submitted document.getClearanceStatusisRegulatoryStatusReader
RegulatoryTransmitter (extends RegulatoryStatusReader)Submit a document to the tax authority for clearance (+ read its status).submitForClearanceisRegulatoryTransmitter
RegulatoryResubmitterRe-trigger transmission of an ALREADY-ISSUED document (e.g. the operator “resend to KSeF” action on a rejected document) — flat, not extends RegulatoryStatusReader.resubmitForClearanceisRegulatoryResubmitter
RegulatoryDocumentReaderRetrieve the authority’s confirmation document (e.g. the PL UPO) or a rendered view for a cleared document.getRegulatoryDocumentisRegulatoryDocumentReader
CorrectionIssuerIssue a correcting document (e.g. KSeF KOR) against an original.issueCorrectionisCorrectionIssuer
OfflineResubmitterRetransmit a document issued with legal effect during a clearance-authority outage (degraded-mode pending-submission) once the authority recovers.resubmitisOfflineResubmitter
RegulatoryRecordLocatorLast-resort crash-recovery lookup: query the authority by business coordinates (seller id, document number, issue-date window) to learn whether an interrupted submit actually landed.locateByQueryisRegulatoryRecordLocator
BankAccountsReaderList the seller’s payable bank accounts known to the provider (live picker for Transfer invoices).listBankAccountsisBankAccountsReader
BankAccountDefaultSetter (extends BankAccountsReader)Mark an account as the provider’s own default, keeping it in sync with the account OL stamps on Transfer invoices.setDefaultBankAccountisBankAccountDefaultSetter
PaymentStatusReaderAuthoritative re-read of a document’s payment state (a provider payment webhook is only a trigger, never trusted as the system of record).getPaymentStatusisPaymentStatusReader
PaymentMarkerPush an authoritative “paid” state to the provider for an order settled elsewhere (e.g. a marketplace order the seller’s bank statement can’t auto-match).markPaidisPaymentMarker
InvoiceEmailSenderTrigger the provider to render and email the already-issued invoice to the buyer.sendByEmailisInvoiceEmailSender
DocumentNumberConsumerMarker: the adapter relies on OpenLinker to allocate the legal, sequential document number from the connection’s numbering series (OL-numbered provider, e.g. KSeF FA(3) P_2). Providers that number documents themselves (inFakt/Subiekt) do NOT implement it.consumesDocumentNumber (marker) · numberingTimeZone · maxDocumentNumberLength?isDocumentNumberConsumer

Adapter coverage: KSeF implements RegulatoryTransmitter and CorrectionIssuer; Infakt implements RegulatoryStatusReader (it relays to KSeF rather than transmitting directly), CorrectionIssuer, RegulatoryDocumentReader, and BankAccountsReader / BankAccountDefaultSetter; Subiekt GT and Subiekt nexo each implement RegulatoryStatusReader, CorrectionIssuer, and BankAccountsReader / BankAccountDefaultSetter — two separate InsERT products on two separate bridges, never one adapter; eparagony.pl implements RegulatoryStatusReader and CorrectionIssuer (#3193), on the same relay split as Infakt for the base document — it hands the document to the national e-invoicing hub on the seller’s behalf and reads back the clearance, never holding the authority session itself — while CorrectionIssuer.issueCorrection composes the vendor’s sibling eCorrectiveInvoice document kind directly. (See the README Integrations section.)

See ADR-026 for the country-agnostic invoicing design.

Sub-capabilityWhat it doesMethod(s)Guard
FiscalRegistrationLocatorResolve an in-doubt outcome by querying the provider for a registration already made under this (connectionId, idempotencyKey) pair — never a resubmit.locateByQueryisFiscalRegistrationLocator

Adapter coverage: eparagony.pl implements FiscalRegistrationLocator. No device/peripheral sub-capability exists: print/fiscalize are booleans inside the vendor’s document payload, not separate operations, and no known adapter exposes fiscal-printer control as a distinct call — see ADR-042 § Decision 5 for why one was designed and then not built. A periodic journal/audit export (FiscalJournalExporter) is the named first extension point for a future adapter, not yet implemented by any shipped one.

See ADR-042 for the fiscalization capability design.

Sub-capabilityWhat it doesMethod(s)Guard
ModifiedProductListerThe modified-since rung of the master capability ladder — enumerate only the external ids whose product record changed after a caller-supplied watermark, instead of the whole catalog.listExternalIdsModifiedSinceisModifiedProductLister

Adapter coverage: WooCommerce only (modified_after + dates_are_gmt=true, WC ≥ 5.8). PrestaShop deliberately does not declare it: a combination write does not bump the parent product’s date_upd, and neither ps_product_attribute nor ps_stock_available carries a mutation timestamp to union against, so a delta path would look healthy while silently skipping every variant-level change (measured in #2221 — see the ADR-048 amendment). A master declaring nothing stays on the base port’s enumerate-only behaviour, which is correct rather than degraded.

Guard-only, unlike the advertised-without-dispatch sub-capabilities above: this name is absent from every adapter manifest and from CoreCapabilityValues, and is resolved solely by narrowing a dispatched ProductMaster adapter. A connection’s enabledCapabilities is stamped at create and never retro-filled, so gating on a newly advertised name would drain nothing for every connection that already exists; not advertising it removes the temptation to gate on it. Consumers of the guard: the worker’s master.product.syncDelta handler and the catalog-trust read (CatalogTrustService, #2258), which reports the declared rung to the operator.

Read caveat: the freshness reported is product-level only. A WooCommerce variation edit does not bump the parent’s date_modified (WC #19562), and stock is not on this rung at all. See ADR-048.


Not a ports table. The six rows below are the independently assignable fulfillment authorities of ADR-052 — “OMS” is not one thing a connection either holds or does not, it is authority assigned per what a party physically controls. They appear here because this is the developer-facing vocabulary reference, and because three of the six are gated by a real adapter capability while three are named by configuration alone (config-only) — a distinction a reader looking up a capability name needs.

owningContext names where resolution and enforcement live (ADR-053); the vocabulary leaf itself imports nothing from those contexts, and two of them (fulfillment, returns) do not exist yet.

Pinned. This table is a mirror of AuthorityKindValues and AUTHORITY_KIND_DESCRIPTORS in libs/core/src/fulfillment-authority/domain/types/authority-kind.types.ts, enforced by scripts/check-authority-kind-mirror.mjs under pnpm check:invariants. The fenced comments below are the parser’s boundary — do not remove them. If the table disagrees with the code, the code wins.

#KindCapabilityConfig keyOwning context
A1availabilityAvailabilityAuthorityavailabilityAuthorityinventory
A2sourcingconfig-onlysourcingAuthorityfulfillment
A3fulfillment-executionFulfillmentExecutorfulfillmentExecutorfulfillment
A4order-lifecycleconfig-onlyorderLifecycleAuthorityorders
A5returns-dispositionReturnsAuthorityreturnsAuthorityreturns
A6refund-triggerconfig-onlyrefundTriggerorders

Seven questions, six kinds. AuthorityKindValues above enumerates what can be assigned; the operator-facing surface asks one more question — “who issues invoices and receipts?” — whose answer is owned by the sales-documents context (ADR-041) and is therefore reached by a link, never mirrored here. The question space is its own vocabulary, AuthorityQuestionValues (#2351), and a spec pins its six non-null kinds to this table’s six, in order.

A6 is never assignable away from OL (ADR-056). Its config key is read so an operator’s claim is observable, never so it can be honoured: an OMS requests a refund and OL executes or refuses with a persisted reason.

There is no A7 row. ADR-052 enumerates six authorities with invoicing/fiscalization “already resolved by ADR-041”, whose router is shipped code. A7 has an owning context already — sales-documents — so it carries no AuthorityKind member. Do not “fix” the count to seven.


  1. Port → libs/core/src/<ctx>/domain/ports/<name>.port.ts; export from the context barrel. If it should be registry-resolvable, decide whether it joins the closed CoreCapabilityValues set or stays an open-world string (#576).
  2. Sub-capability → libs/core/src/<ctx>/domain/ports/capabilities/<name>.capability.ts: the interface plus the co-located is{Capability} guard. Export both from the context barrel.
  3. Update this file in the same PR — add the row(s) above. The counts in each section heading are part of the contract; bump them.