Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/VisualGraphxLLC/API-HUB/llms.txt

Use this file to discover all available pages before exploring further.

Suppliers in API-HUB are database records, not code. Adding a new wholesale source means inserting a row in the suppliers table with the right protocol and adapter class — no deployment required. The adapter registry resolves that class name at runtime and hands the import orchestrator a live adapter instance ready to discover and hydrate products.

What a supplier is

Each supplier record describes a single wholesale data source. It holds the credentials needed to authenticate, the protocol used to communicate, and the adapter class that knows how to speak that protocol. The platform currently supports two protocols:
  • promostandards — SOAP-based communication via zeep + lxml. Used by the 994+ suppliers in the PromoStandards directory, including SanMar.
  • rest_hmac — REST API with HMAC-SHA256 request signing. Used by 4Over and similar print suppliers.
The auth_config column is stored as EncryptedJSON — AES-128 via Fernet — so credentials never appear in plaintext in the database. The protocol_config column (plain JSONB) holds non-sensitive settings like explicit product ID lists or service version overrides.

Supplier fields

FieldTypePurpose
namestringHuman-readable display name
slugstringURL-safe unique identifier
protocolstringpromostandards or rest_hmac
promostandards_codestringPS directory code (PS suppliers only)
base_urlstringRoot API URL (REST suppliers)
adapter_classstringRegistry key — e.g. SanMarAdapter
auth_configEncryptedJSONCredentials: id/password or api_key/private_key
protocol_configJSONBNon-sensitive protocol settings
endpoint_cacheJSONBCached WSDL URLs from PS directory
field_mappingsJSONBCustom field overrides for this supplier
is_activebooleanControls whether sync jobs run
last_full_synctimestampSet after a completed full import
last_delta_synctimestampSet after a completed delta import
has_decoration_overlaybooleanSignals OPS to render decoration areas
push_name_prefixstringOptional prefix prepended to product names in OPS

Built-in adapters

PromoStandardsAdapter

Generic SOAP adapter for any supplier in the PromoStandards directory. Calls getProductSellable, getProduct, getConfigurationAndPricing, and getMediaContent via zeep. Set protocol: promostandards and adapter_class: PromoStandardsAdapter.

SanMarAdapter

Subclass of PromoStandardsAdapter with hardcoded WSDL fallbacks for SanMar’s four PS services (Product v2.0.0, Media v1.1.0, Pricing v1.0.0, Inventory v2.0.0). Also supports SanMar’s non-standard category extension WSDL. Set adapter_class: SanMarAdapter.

FourOverAdapter

REST adapter for 4Over’s HMAC-SHA256-signed API. Discovers print products via GET /products, fetches option groups per product. Set protocol: rest_hmac and adapter_class: FourOverAdapter. Auth config keys: api_key and private_key.

OPSAdapter

Reads products inbound from an OnPrintShop storefront via GraphQL. Normalizes to product_type: print. Used when OPS itself is a data source rather than a push target. Set adapter_class: OPSAdapter.

How the adapter registry works

Every adapter module calls register_adapter(name, cls) at import time. main.py imports each module on startup, which populates the ADAPTERS dict before any route is served.
# modules/import_jobs/registry.py
ADAPTERS: dict[str, Type[BaseAdapter]] = {}

def register_adapter(name: str, cls: Type[BaseAdapter]) -> None:
    ADAPTERS[name] = cls

def get_adapter(supplier, db: AsyncSession) -> BaseAdapter:
    adapter_key = getattr(supplier, "adapter_class", None)
    if not adapter_key:
        raise AdapterNotConfiguredError(...)
    cls = ADAPTERS.get(adapter_key)
    if cls is None:
        raise AdapterNotRegisteredError(...)
    return cls(supplier=supplier, db=db)
When an import job starts, the orchestrator calls get_adapter(supplier, db). The registry looks up supplier.adapter_class in ADAPTERS, instantiates the class with the supplier record and a database session, and returns a live adapter. Two things can go wrong:
  • AdapterNotConfiguredErroradapter_class is NULL. Set it via PATCH /api/suppliers/{id} or the admin UI before triggering an import.
  • AdapterNotRegisteredErroradapter_class has a value that no module registered. This usually means a typo or a module that was not imported on startup.

Supplier lifecycle

Created (is_active=False)


  Test connection ─── POST /api/suppliers/test
       │ ok

  Activate (is_active=True)


  First import ─── POST /api/suppliers/{id}/import  mode=first_n, limit=5


  Scheduled delta syncs (n8n workflow, daily by default)


  Deactivated (is_active=False) ─── no sync jobs run
Setting is_active: false suspends all scheduled sync jobs for the supplier without deleting any catalog data. You can reactivate the supplier at any time by patching is_active back to true.
Deleting a supplier via DELETE /api/suppliers/{id} permanently removes all associated products, variants, images, categories, and sync job history. This action cannot be undone.

Supplier endpoints reference

MethodPathDescription
GET/api/suppliersList all suppliers with product counts
POST/api/suppliersCreate or reactivate a supplier
GET/api/suppliers/{id_or_slug}Get a single supplier by UUID or slug
PATCH/api/suppliers/{id_or_slug}Update patchable fields
DELETE/api/suppliers/{id_or_slug}Delete supplier and all catalog data
POST/api/suppliers/testTest connection before saving
GET/api/suppliers/{id}/endpointsFetch cached PS endpoint WSDLs
PUT/api/suppliers/{id}/mappingsSave field mapping overrides
POST/api/suppliers/{id}/importTrigger a manual import job
GET/api/suppliers/{id}/sync-jobsList import history for a supplier

Build docs developers (and LLMs) love