Alter Product Public API — integracja

Public API służy do integracji server-to-server ze storefrontami, backendami sklepów, pluginami WordPress/WooCommerce i zewnętrznymi procesami produkcyjnymi.

Uwierzytelnianie i base URL

https://alterproduct.com/public-api/v1

Dane dostępowe API wygenerujesz w ustawieniach e-commerce. Access Token jest pokazywany tylko raz, więc od razu zapisz go w backendowym storage sekretów.

Access Key i Access Token trzymaj wyłącznie po stronie serwera. Uwierzytelnione endpointy odrzucają wywołania przeglądarkowe z nagłówkami Origin lub Referer.

Dane dostępowe mogą mieć scope’y. Użyj GET /auth/check, żeby sprawdzić aktywny storefront, możliwości planu i scope’y przypisane do credentiala.

x-alter-access-key: YOUR_API_KEY
x-alter-access-token: YOUR_API_TOKEN
ParametrWymaganySzczegóły
x-alter-access-keytakPubliczny identyfikator credentiala.
x-alter-access-tokentakSekretny token sparowany z access key.
x-alter-client-fingerprintnieOpcjonalny stabilny fingerprint używany przy limitach sesji embed.
Authorizationtylko runtimeToken Bearer zwrócony przez POST /embed/session, używany przez /runtime/bootstrap.

Test połączenia

Użyj endpointu auth check przed włączeniem synchronizacji lub funkcji embed w produkcyjnej integracji.

GET https://alterproduct.com/public-api/v1/auth/check

Przykładowe zapytanie (fetch)

const response = await fetch('https://alterproduct.com/public-api/v1/auth/check', {
  method: 'GET',
  headers: {
    'x-alter-access-key': process.env.ALTER_ACCESS_KEY,
    'x-alter-access-token': process.env.ALTER_ACCESS_TOKEN
  }
});

const payload = await response.json();

if (!response.ok) {
  throw new Error(payload?.code || payload?.error || `Alter API ${response.status}`);
}

console.log(payload);

Przykładowa odpowiedź

{
  "ok": true,
  "message": "success",
  "storefrontId": 12,
  "userOwnerId": 34,
  "credentialId": 56,
  "scopes": ["orders:read", "orders:write", "products:read"],
  "plan": {
    "requiredPlan": "Business",
    "currentPlanName": "Business",
    "eligible": true,
    "runtimeFlags": {
      "viewer": true,
      "configurator": true,
      "customizer": true
    },
    "limits": {
      "activeRuntimeBindingsLimit": 100,
      "monthlyReassignmentLimit": 1000,
      "monthlyEmbedTokenLimit": 50000
    }
  }
}

Poniższy helper jest używany w pozostałych przykładach. To zwykły fetch i działa w Node.js 18+ lub dowolnym runtime serwerowym z fetch.

const ALTER_API_BASE = 'https://alterproduct.com/public-api/v1';

const authHeaders = {
  'x-alter-access-key': process.env.ALTER_ACCESS_KEY,
  'x-alter-access-token': process.env.ALTER_ACCESS_TOKEN
};

async function alterFetch(path, options = {}) {
  const response = await fetch(`${ALTER_API_BASE}${path}`, {
    ...options,
    headers: {
      ...authHeaders,
      ...(options.body ? { 'Content-Type': 'application/json' } : {}),
      ...options.headers
    }
  });

  const payload = await response.json().catch(() => null);

  if (!response.ok) {
    throw new Error(payload?.code || payload?.error || `Alter API ${response.status}`);
  }

  return payload;
}

Przegląd endpointów

Tabela odzwierciedla publiczne routy montowane w backend-public-api/app.js. Ścieżki pokazują publiczny prefiks reverse proxy używany przez integracje zewnętrzne.

MetodaEndpointOpisDostęp
GET/public-api/healthzHealth check serwisu.publiczny
GET/public-api/v1/auth/checkWaliduje credentiale i zwraca storefront, scope’y oraz możliwości planu.dowolny uwierzytelniony credential
GET/public-api/v1/customer-ordersZwraca paginowaną i filtrowalną listę zamówień klientów.orders:read
GET/public-api/v1/customer-orders/:idZwraca pojedyncze zamówienie klienta ze skonfigurowanymi pozycjami.orders:read
POST/public-api/v1/customer-orders/batchZwraca maksymalnie 100 zamówień na podstawie ID.orders:read
PATCH/public-api/v1/customer-orders/:id/statusAktualizuje status zamówienia.orders:write
PATCH/public-api/v1/customer-orders/:orderId/quantityAktualizuje ilości wybranych pozycji zamówienia.orders:write
PATCH/public-api/v1/customer-orders/:orderId/quantity/allUstawia jedną ilość dla wszystkich pozycji zamówienia.orders:write
DELETE/public-api/v1/customer-orders/:idUsuwa zamówienie klienta należące do właściciela storefrontu.orders:write
GET/public-api/v1/productsZwraca produkty/designy storefrontu z dostępnością embed i URL-ami mediów.products:read
GET/public-api/v1/products/:idZwraca pojedynczy produkt/design storefrontu.products:read
POST/public-api/v1/embed/sessionWystawia krótko żyjący JWT dla runtime designer/viewer/configurator/customizer.embed:session:create
GET/public-api/v1/runtime/bootstrapRozwiązuje kontekst runtime z tokenu embed JWT.token embed Bearer
GET/public-api/v1/assetsListuje assety katalogu dla wskazanego typu.dowolny uwierzytelniony credential
GET/public-api/v1/assets/:type/:assetIdZwraca manifest assetu z rolami plików do pobrania.dowolny uwierzytelniony credential
GET/public-api/v1/assets/:type/:assetId/files/:rolePobiera plik assetu po roli.dowolny uwierzytelniony credential
GET/public-api/v1/design-importsListuje designy dostępne do importu.uwierzytelniony credential, wymagany plan Business
GET/public-api/v1/design-imports/:idZwraca payload importu designu i deskryptory plików.uwierzytelniony credential, wymagany plan Business
GET/public-api/v1/design-imports/:id/files/:fileIdPobiera plik z deskryptora importu designu.uwierzytelniony credential, wymagany plan Business
GET/public-api/v1/file/public/products/:productId/:sizeZwraca publiczny podgląd produktu. Size musi mieć wartość small.png, medium.png albo big.png.publiczny
GET/public-api/v1/file/protected/:keyZwraca chroniony plik po storage key.podpisany URL albo files:read
GET/public-api/v1/fontsZwraca wszystkie dostępne fonty.publiczny
GET/public-api/v1/currenciesZwraca wszystkie waluty.publiczny
POST/public-api/v1/runtime-bindings/sync-from-wordpressTworzy lub aktualizuje runtime bindings z mapowań produktów WordPress.dowolny uwierzytelniony credential
PATCH/public-api/v1/runtime-bindings/:idAktualizuje runtime binding.dowolny uwierzytelniony credential
POST/public-api/v1/runtime-bindings/:id/activateAktywuje runtime binding.dowolny uwierzytelniony credential
POST/public-api/v1/runtime-bindings/:id/deactivateDezaktywuje runtime binding.dowolny uwierzytelniony credential
POST/public-api/v1/wp-connect/exchangeWymienia kod handoff WordPress auto-connect na credentiale API.jednorazowy kod handoff

Zamówienia klientów

Endpointy zamówień pozwalają zewnętrznemu sklepowi odczytać skonfigurowane pozycje, zmienić ilości, przesuwać zamówienie między statusami realizacji i usuwać porzucone zamówienia.

ParametrWymaganySzczegóły
namenieSzuka po nazwie designu oraz numerycznym ID zamówienia.
category_idnieFiltruje po ID kategorii produktu.
order_statusnieJedna z dozwolonych wartości statusu.
offsetnieDomyślnie 0. Musi być >= 0.
limitnieDomyślnie 9 dla tego kontrolera, maksymalnie 50.
order_bynieid, created_at albo design_name.
directionnieASC albo DESC.

Przykładowe zapytanie (fetch)

const params = new URLSearchParams({
  limit: '20',
  offset: '0',
  order_status: 'shopping_cart',
  order_by: 'created_at',
  direction: 'DESC'
});

const orders = await alterFetch(`/customer-orders?${params.toString()}`);

const order = await alterFetch('/customer-orders/123');

const batch = await alterFetch('/customer-orders/batch', {
  method: 'POST',
  body: JSON.stringify({
    customerOrderIds: [123, 124, 125]
  })
});

Dozwolone wartości

StatusOpis
shopping_cartFlow koszyka; klient nadal może edytować konfigurację.
editableZamówienie pozostaje edytowalne przez klienta.
paidZamówienie jest opłacone i gotowe do realizacji.
processingZamówienie jest w realizacji.
completedZamówienie zostało zrealizowane.
cancelledZamówienie zostało anulowane.

Przykładowe zapytanie (fetch)

await alterFetch('/customer-orders/123/status', {
  method: 'PATCH',
  body: JSON.stringify({
    status: 'processing'
  })
});

await alterFetch('/customer-orders/123/quantity', {
  method: 'PATCH',
  body: JSON.stringify({
    items: [
      { orderDetailId: 987, quantity: 3 }
    ]
  })
});

await alterFetch('/customer-orders/123/quantity/all', {
  method: 'PATCH',
  body: JSON.stringify({
    quantity: 2
  })
});

await alterFetch('/customer-orders/123', {
  method: 'DELETE'
});

Przykładowa odpowiedź

{
  "order": {
    "id": 123,
    "customizerId": 381,
    "orderStatus": "shopping_cart",
    "createdAt": "2026-05-28T10:15:00.000Z",
    "customizerOrderURL": "https://alterproduct.com/app/customizer/381/123",
    "productItems": [
      {
        "id": 987,
        "model3d": { "id": 381 },
        "size": {
          "id": 395,
          "name": { "pl": "M", "en": "M" },
          "measureSize": null
        },
        "material": {
          "id": 2,
          "name": { "pl": "Bawełna", "en": "Cotton" }
        },
        "printType": {
          "id": 1,
          "name": { "pl": "DTG", "en": "DTG" }
        },
        "color": {
          "id": 418,
          "name": { "pl": "Domyślny", "en": "Default" },
          "hex": "#ffffff"
        },
        "variant": {
          "id": 531,
          "metadata": null,
          "stockQuantity": 25
        },
        "unitPrice": { "value": 12.5, "currency": "EUR" },
        "totalPrice": { "value": 37.5, "currency": "EUR" },
        "quantity": 3
      }
    ],
    "customizerName": "Men's T-Shirt",
    "productGroup": {
      "id": 4,
      "name": { "pl": "Koszulka", "en": "T-Shirt" }
    },
    "totalPrice": { "value": 37.5, "currency": "EUR" }
  }
}

Produkty storefrontu

Endpointy produktów zwracają designy storefrontu, które można osadzać jako viewer, configurator albo customizer.

ParametrWymaganySzczegóły
namenieSzuka po nazwie produktu/designu.
customizernietrue albo false.
offsetnieDomyślnie 0. Musi być >= 0.
limitnieDomyślnie 9, maksymalnie 50.
order_bynieid, name albo created_at.
directionnieASC albo DESC.

Przykładowe zapytanie (fetch)

const params = new URLSearchParams({
  limit: '20',
  offset: '0',
  name: 't-shirt',
  customizer: 'true',
  order_by: 'created_at',
  direction: 'DESC'
});

const products = await alterFetch(`/products?${params.toString()}`);
const product = await alterFetch('/products/381');

Przykładowa odpowiedź

{
  "products": {
    "items": [
      {
        "id": 381,
        "name": "Men's T-Shirt",
        "createdAt": "2026-01-03T23:55:05.000Z",
        "productId": 4,
        "media": {
          "img": {
            "big": "https://alterproduct.com/public-api/v1/file/public/products/4/big.png",
            "medium": "https://alterproduct.com/public-api/v1/file/public/products/4/medium.png",
            "small": "https://alterproduct.com/public-api/v1/file/public/products/4/small.png"
          },
          "mockups": []
        },
        "storefrontProduct": {
          "id": 89,
          "idUserDesign": 381,
          "shareAccess": "public",
          "isCustomizer": 1
        },
        "runtimeBindings": [
          {
            "id": 42,
            "runtimeType": "customizer",
            "status": "active",
            "externalProductId": "wc_123"
          }
        ],
        "embeddable": {
          "viewer": true,
          "configurator": true,
          "customizer": true
        }
      }
    ],
    "total": 1
  }
}

Sesje embed i runtime bootstrap

Utwórz krótko żyjący token embed po stronie serwera, przekaż go do iframe/runtime, a następnie runtime pobiera kontekst przez bootstrap z tokenem Bearer.

ParametrWymaganySzczegóły
runtimeBindingIdzalecanyPreferowany identyfikator aktywnego runtime bindingu.
toolwymagany bez runtimeBindingIddesigner, viewer, configurator albo customizer.
origintakOrigin, na którym renderowany jest embed, np. https://yourstore.com.
designIdjeden identyfikatorID designu Alter Product. Nie łącz z orderId.
orderIdjeden identyfikatorID zamówienia customizera. Działa tylko dla customizera.
cartKey + cartModenieKontekst koszyka tylko dla customizera. cartMode to view albo edit.

Przykładowe zapytanie (fetch)

const session = await alterFetch('/embed/session', {
  method: 'POST',
  headers: {
    'x-alter-client-fingerprint': '9f1b7a5e4b3c2d1f9f1b7a5e4b3c2d1f'
  },
  body: JSON.stringify({
    runtimeBindingId: 42,
    origin: 'https://yourstore.com'
  })
});

const bootstrapResponse = await fetch('https://alterproduct.com/public-api/v1/runtime/bootstrap', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${session.token}`
  }
});

const bootstrap = await bootstrapResponse.json();
console.log({ session, bootstrap });

Uwagi

await alterFetch('/embed/session', {
  method: 'POST',
  body: JSON.stringify({
    tool: 'customizer',
    origin: 'https://yourstore.com',
    orderId: 123
  })
});

await alterFetch('/embed/session', {
  method: 'POST',
  body: JSON.stringify({
    tool: 'viewer',
    origin: 'https://yourstore.com',
    designId: 381
  })
});

Przykładowa odpowiedź

{
  "token": "eyJhbGciOiJIUzI1NiIsImtpZCI6IjEifQ...",
  "expiresIn": 900,
  "kid": "1",
  "mode": "design",
  "runtimeBindingId": 42,
  "runtimeType": "customizer"
}

Runtime bootstrap

{
  "runtimeBindingId": 42,
  "designId": 381,
  "productId": "wc_123",
  "runtimeType": "customizer",
  "storageMode": "wordpress_local",
  "manifestUrl": "https://yourstore.com/wp-content/uploads/alter/381/manifest.json",
  "assetBaseUrl": "https://yourstore.com/wp-content/uploads/alter/381/",
  "manifestHash": "a3b1...",
  "planCapabilities": {
    "viewer": true,
    "configurator": true,
    "customizer": true
  },
  "cartKey": null,
  "cartMode": null,
  "orderId": null
}

Katalog assetów

Katalog assetów udostępnia produkty bazowe, tła, środowiska, bibliotekę grafik, szablony designów i assety mockupów. Lista zwraca lekkie deskryptory, a szczegóły zawierają manifest plików.

TypOpis
productsAssety produktów bazowych, podglądy, modele 3D oraz deskryptory materiałów i tekstur.
backgroundsStatyczne tła viewera.
environmentsMapy środowisk i obrazy podglądu.
image_libraryAssety biblioteki grafik, także grafiki scoped do storefrontu.
design_templatesPodglądy szablonów designów i referencje plików warstw. Obsługuje filtr product_id.
mockupsAssety generatora mockupów, tła i mapy overlay. Obsługuje filtr product_id.

Przykładowe zapytanie (fetch)

const assets = await alterFetch('/assets?' + new URLSearchParams({
  type: 'products',
  limit: '20',
  offset: '0',
  search: 'mug'
}));

const details = await alterFetch('/assets/products/4');

const fileResponse = await fetch(
  'https://alterproduct.com/public-api/v1/assets/products/4/files/preview_medium',
  {
    headers: authHeaders
  }
);

const fileBlob = await fileResponse.blob();

Przykładowa odpowiedź

{
  "type": "products",
  "items": [
    {
      "assetType": "products",
      "assetId": "4",
      "title": "Mug 450ml",
      "slug": "product-4",
      "description": "Base product 4",
      "primaryRole": "preview_big",
      "fileCount": 8,
      "remoteVersion": "1.0",
      "thumbnail": {
        "role": "preview_small",
        "fileName": "product-4-preview-small.png",
        "mime": "image/png",
        "downloadPath": "/v1/assets/products/4/files/preview_small"
      },
      "metadata": {
        "productCategoryId": 2,
        "productModelCount": 1,
        "isDedicated": false
      }
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}

Importy designów

Importy designów udostępniają designy hostowane w Alter Product oraz ich pliki dla zewnętrznej produkcji lub migracji. API sprawdza dostępność funkcji w planie Business.

ParametrWymaganySzczegóły
searchnieSzuka po tytule designu lub ID.
offsetnieDomyślnie 0.
limitnieDomyślnie 20, maksymalnie 100.

Przykładowe zapytanie (fetch)

const imports = await alterFetch('/design-imports?' + new URLSearchParams({
  limit: '20',
  offset: '0',
  search: 'mug'
}));

const details = await alterFetch('/design-imports/381');

const fileId = details.files[0].id;
const fileResponse = await fetch(
  `https://alterproduct.com/public-api/v1/design-imports/381/files/${fileId}`,
  {
    headers: authHeaders
  }
);

const fileBlob = await fileResponse.blob();

Przykładowa odpowiedź

{
  "eligible": true,
  "requiredPlan": "Business",
  "currentPlanName": "Business",
  "designs": [
    {
      "id": 381,
      "title": "Men's T-Shirt",
      "createdAt": "2026-01-03T23:55:05.000Z",
      "sourceStorefrontId": 12,
      "productId": 4,
      "productName": {
        "pl": "Koszulka",
        "en": "T-Shirt"
      },
      "storageMode": "alter",
      "runtimeStatus": {
        "designer": true,
        "viewer": true,
        "configurator": true,
        "customizer": true
      },
      "thumbnail": {
        "kind": "design-mockup",
        "fileId": "7df7...",
        "downloadPath": "/v1/design-imports/381/files/7df7..."
      }
    }
  ],
  "total": 1
}

Pliki, fonty i waluty

Publiczne pliki podglądu są przyjazne dla przeglądarki. Chronione pliki wymagają podpisanego URL-a albo credentiala API ze scope files:read. Fonty i waluty są publicznymi endpointami read.

EndpointDostępSzczegóły
/file/public/products/:productId/small.pngpublicznyMały podgląd produktu.
/file/public/products/:productId/medium.pngpublicznyŚredni podgląd produktu.
/file/public/products/:productId/big.pngpublicznyDuży podgląd produktu.
/file/protected/:keypodpisany URL albo files:readChroniony plik ze storage.
/fontspublicznyTablica rekordów fontów.
/currenciespublicznyTablica rekordów walut.

Przykładowe zapytanie (fetch)

const publicPreview = await fetch(
  'https://alterproduct.com/public-api/v1/file/public/products/4/medium.png'
);

const protectedFile = await fetch(
  'https://alterproduct.com/public-api/v1/file/protected/user_34/381/design/mockup-large.webp',
  {
    headers: authHeaders
  }
);

const fonts = await fetch('https://alterproduct.com/public-api/v1/fonts').then((res) => res.json());
const currencies = await fetch('https://alterproduct.com/public-api/v1/currencies').then((res) => res.json());

Przykładowa odpowiedź

[
  {
    "id": 1,
    "family": "Inter",
    "source": "google",
    "category": "sans-serif",
    "variants": ["regular", "600", "700"],
    "subsets": ["latin"],
    "version": "v19",
    "menu": "Inter",
    "files": {
      "regular": "https://..."
    }
  }
]
[
  {
    "id": 1,
    "code": "EUR",
    "name": "Euro",
    "symbol": "€",
    "decimalPlaces": 2
  }
]

Runtime bindings

Runtime bindings łączą zewnętrzne produkty commerce z designami Alter Product i typami runtime. Używają ich głównie integracje WordPress/WooCommerce oraz zaawansowane backendy storefrontu.

ParametrWymaganySzczegóły
designIdnieID designu Alter Product należącego do storefrontu.
externalProductIdtak dla syncZewnętrzne ID produktu, np. ID produktu WooCommerce.
runtimeTypetak dla syncviewer, configurator albo customizer.
statusniedraft, active, inactive, archived albo legacy_active.
legacyStorefrontProductIdnieOpcjonalne ID legacy mappingu.
legacyBindingMetanieOpcjonalne metadane JSON, np. manifestHash.

Przykładowe zapytanie (fetch)

await alterFetch('/runtime-bindings/sync-from-wordpress', {
  method: 'POST',
  body: JSON.stringify({
    bindings: [
      {
        externalProductId: 'wc_123',
        runtimeType: 'customizer',
        status: 'active',
        designId: 381,
        legacyBindingMeta: {
          manifestHash: 'a3b1...'
        }
      }
    ]
  })
});

await alterFetch('/runtime-bindings/42', {
  method: 'PATCH',
  body: JSON.stringify({
    status: 'inactive'
  })
});

await alterFetch('/runtime-bindings/42/activate', { method: 'POST' });
await alterFetch('/runtime-bindings/42/deactivate', { method: 'POST' });

wordpress_local

await alterFetch('/runtime-bindings/sync-from-wordpress', {
  method: 'POST',
  body: JSON.stringify({
    bindings: [
      {
        externalProductId: 'wc_123',
        runtimeType: 'viewer',
        status: 'active',
        externalDesign: {
          externalDesignKey: 'wp-design-381',
          productId: 4,
          title: 'WooCommerce local design',
          manifestUrl: 'https://yourstore.com/wp-content/uploads/alter/381/manifest.json',
          assetBaseUrl: 'https://yourstore.com/wp-content/uploads/alter/381/',
          manifestHash: 'a3b1...',
          sourceMeta: {
            pluginVersion: '1.2.0'
          }
        }
      }
    ]
  })
});

Przykładowa odpowiedź

{
  "message": "runtimeBinding.syncCompleted",
  "runtimeBindings": [
    {
      "id": 42,
      "designId": 381,
      "externalProductId": "wc_123",
      "runtimeType": "customizer",
      "status": "active"
    }
  ]
}

WordPress connect exchange

Endpoint WordPress connect exchange zużywa jednorazowy kod handoff i zwraca credentiale API do pluginu. To nie jest ogólny endpoint do tworzenia credentiali.

Przykładowe zapytanie (fetch)

const response = await fetch('https://alterproduct.com/public-api/v1/wp-connect/exchange', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    code: 'ONE_TIME_HANDOFF_CODE',
    storeUrl: 'https://yourstore.com/',
    siteOrigin: 'https://yourstore.com',
    codeVerifier: 'PKCE_CODE_VERIFIER_32_TO_128_CHARS'
  })
});

const credentials = await response.json();

Przykładowa odpowiedź

{
  "message": "wpConnect.exchange.ok",
  "accessKey": "generated-access-key",
  "accessToken": "generated-access-token",
  "storefrontId": 12
}

Błędy i limity zapytań

Większość błędów kontrolerów jest normalizowana do odpowiedzi z code. Middleware uwierzytelniania i limitery mogą zwrócić odpowiedź z error.

// Controller error
{
  "code": "assetCatalog.invalidType"
}

// Auth middleware or rate limit
{
  "error": "Unauthorized"
}

{
  "error": "Too Many Requests"
}
TypLimitOkno
Globalnie600 zapytań60 sekund
GET /auth/check60 zapytań60 sekund
Orders read/products read300 zapytań60 sekund
Orders write/embed sessions/runtime bindings120 zapytań60 sekund
Assets/design imports read180 zapytań60 sekund
Fonts300 zapytań60 sekund
WP connect exchange30 zapytań60 sekund