Intégration de l’API publique Alter Product

L’API publique est conçue pour les intégrations de serveur à serveur avec les boutiques, les systèmes e-commerce, les extensions WordPress/WooCommerce et les processus de production externes.

Authentification et URL de base

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

Créez des identifiants API dans le panneau des paramètres e-commerce. Le jeton d’accès n’est affiché qu’une fois : enregistrez-le immédiatement dans le stockage sécurisé des secrets de votre serveur.

Conservez la clé d’accès et le jeton d’accès sur votre serveur. Les points d’accès authentifiés rejettent les appels provenant du navigateur qui incluent les en-têtes Origin ou Referer.

Les identifiants peuvent avoir des portées limitées. Utilisez GET /auth/check pour vérifier la boutique active, les fonctionnalités de l’offre et les portées renvoyées pour l’identifiant.

x-alter-access-key: YOUR_API_KEY
x-alter-access-token: YOUR_API_TOKEN
ParamètreObligatoireDétails
x-alter-access-keyouiIdentifiant public des accès.
x-alter-access-tokenouiJeton secret associé à la clé d’accès.
x-alter-client-fingerprintnonEmpreinte stable facultative pour la limitation des requêtes de session d’intégration.
Authorizationenvironnement d’exécution uniquementJeton Bearer renvoyé par POST /embed/session, utilisé par /runtime/bootstrap.

Test de connexion

Utilisez le point d’accès de vérification de l’authentification avant d’activer la synchronisation ou les fonctions d’intégration en production.

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

Exemple de requête (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);

Exemple de réponse

{
  "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
    }
  }
}

La fonction utilitaire ci-dessous est utilisée dans les exemples suivants. Elle repose sur fetch natif et peut s’exécuter dans Node.js 18+ ou dans tout environnement serveur fournissant 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;
}

Vue d’ensemble des points d’accès

Le tableau ci-dessous reprend les routes publiques déclarées dans backend-public-api/app.js. Les chemins sont affichés avec le préfixe du proxy public utilisé par les intégrations externes.

MéthodePoint d’accèsDescriptionAccès
GET/public-api/healthzVérification de la disponibilité du service.public
GET/public-api/v1/auth/checkValide les identifiants et renvoie la boutique, les portées et les fonctionnalités de l’offre.tout identifiant authentifié
GET/public-api/v1/customer-ordersRenvoie une liste paginée et filtrable des commandes clients.orders:read
GET/public-api/v1/customer-orders/:idRenvoie une commande client avec ses articles configurés.orders:read
POST/public-api/v1/customer-orders/batchRenvoie jusqu’à 100 commandes par identifiant.orders:read
PATCH/public-api/v1/customer-orders/:id/statusMet à jour le statut de la commande.orders:write
PATCH/public-api/v1/customer-orders/:orderId/quantityMet à jour les quantités des lignes de commande sélectionnées.orders:write
PATCH/public-api/v1/customer-orders/:orderId/quantity/allDéfinit une même quantité pour chaque article d’une commande.orders:write
DELETE/public-api/v1/customer-orders/:idSupprime une commande client appartenant au propriétaire de la boutique.orders:write
GET/public-api/v1/productsRenvoie les produits/designs de la boutique avec les possibilités d’intégration et les URL des médias.products:read
GET/public-api/v1/products/:idRenvoie un produit/design de la boutique.products:read
POST/public-api/v1/embed/sessionÉmet un JWT de courte durée pour les outils intégrés, dont le générateur de modèles.embed:session:create
GET/public-api/v1/runtime/bootstrapDétermine le contexte d’exécution à partir d’un JWT d’intégration.jeton d’intégration Bearer
GET/public-api/v1/assetsListe les éléments du catalogue de ressources du type demandé.tout identifiant authentifié
GET/public-api/v1/assets/:type/:assetIdRenvoie le manifeste d’une ressource avec les rôles de ses fichiers téléchargeables.tout identifiant authentifié
GET/public-api/v1/assets/:type/:assetId/files/:roleTélécharge un fichier de ressource selon son rôle.tout identifiant authentifié
GET/public-api/v1/design-importsListe les designs hébergés chez Alter pouvant être importés.identifiant authentifié, offre Business requise
GET/public-api/v1/design-imports/:idRenvoie les données d’import d’un design et les descripteurs de fichiers.identifiant authentifié, offre Business requise
GET/public-api/v1/design-imports/:id/files/:fileIdTélécharge un fichier à partir d’un descripteur d’import de design.identifiant authentifié, offre Business requise
GET/public-api/v1/file/public/products/:productId/:sizeRenvoie un aperçu public de produit. La taille doit être small.png, medium.png ou big.png.public
GET/public-api/v1/file/protected/:keyRenvoie un fichier protégé à partir de sa clé de stockage.URL signée ou files:read
GET/public-api/v1/fontsRenvoie toutes les polices disponibles.public
GET/public-api/v1/currenciesRenvoie toutes les devises.public
POST/public-api/v1/runtime-bindings/sync-from-wordpressCrée ou met à jour les associations d’exécution à partir des associations de produits WordPress.tout identifiant authentifié
PATCH/public-api/v1/runtime-bindings/:idModifie une association d’exécution.tout identifiant authentifié
POST/public-api/v1/runtime-bindings/:id/activateActive une association d’exécution.tout identifiant authentifié
POST/public-api/v1/runtime-bindings/:id/deactivateDésactive une association d’exécution.tout identifiant authentifié
POST/public-api/v1/wp-connect/exchangeÉchange un code de transfert de connexion automatique WordPress contre des identifiants API.code de transfert à usage unique
GET/public-api/v1/model-generator/catalogListe les produits du générateur, les configurations et les révisions de gabarits visibles.embed:session:create
GET/public-api/v1/model-generator/modelsListe les modèles avec des descripteurs de sources du générateur liés à des révisions précises pour l’import.embed:session:create
GET/public-api/v1/model-generator/designer-catalogRenvoie le catalogue de modèles du générateur utilisé par Designer.embed:session:create
GET/public-api/v1/model-generator/projectsListe les projets du générateur appartenant au propriétaire et ceux disponibles globalement.embed:session:create
GET/public-api/v1/model-generator/projects/:projectIdRenvoie la révision la plus récente ou sélectionnée du projet, le gabarit et le manifeste des fichiers.embed:session:create
GET/public-api/v1/model-generator/projects/:projectId/revisions/:revisionRenvoie la révision la plus récente ou sélectionnée du projet, le gabarit et le manifeste des fichiers.embed:session:create
GET/public-api/v1/model-generator/projects/:projectId/artifacts/:artifactIdTélécharge un artefact après vérification de l’accès à son projet.embed:session:create
GET/public-api/v1/model-generator/configurations/:configurationId/revisions/:revision/templateRenvoie un document de gabarit accessible pour la révision de configuration sélectionnée.embed:session:create
GET/public-api/v1/model-generator/configurations/:configurationId/revisions/:revision/importRenvoie le paquet d’import du gabarit avec les fichiers de dépendances.embed:session:create
GET/public-api/v1/model-generator/mannequinsRenvoie les deux mannequins et les descripteurs de leurs ressources.embed:session:create
GET/public-api/v1/model-generator/image-libraries/:kind/assetsListe les ressources des bibliothèques de textures ou d’arrière-plans avec les fichiers importables.embed:session:create
GET/public-api/v1/model-generator/image-libraries/:kind/assets/:assetIdRenvoie une ressource de texture ou d’arrière-plan avec ses fichiers importables.embed:session:create
GET/public-api/v1/model-generator/public-files/:keyTélécharge un fichier de dépendance autorisé du générateur.embed:session:create

Commandes clients

Les points d’accès des commandes clients permettent à une boutique externe de lire les lignes configurées, de modifier les quantités, de faire évoluer les statuts de traitement d’une commande et de supprimer les commandes abandonnées.

ParamètreObligatoireDétails
namenonRecherche le nom du design et l’identifiant numérique de la commande.
category_idnonFiltre par identifiant de catégorie de produit.
order_statusnonL’un des statuts de commande autorisés.
offsetnonValeur par défaut : 0. Doit être >= 0.
limitnonValeur par défaut pour ce contrôleur : 9, maximum : 50.
order_bynonid, created_at ou design_name.
directionnonASC ou DESC.

Exemple de requête (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]
  })
});

Valeurs autorisées

StatutDescription
shopping_cartParcours du panier ; le client peut encore modifier la configuration.
editableLa commande reste modifiable par le client.
paidLa commande est payée et prête à être exécutée.
processingLa commande est en cours de traitement.
completedLa commande a été exécutée.
cancelledLa commande a été annulée.

Exemple de requête (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'
});

Exemple de réponse

{
  "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" }
  }
}

Produits de la boutique

Les points d’accès des produits renvoient les designs de la boutique pouvant être intégrés dans Viewer, Configurator ou Customizer.

ParamètreObligatoireDétails
namenonRecherche le nom du produit/design.
customizernontrue ou false.
offsetnonValeur par défaut : 0. Doit être >= 0.
limitnonValeur par défaut : 9, maximum : 50.
order_bynonid, name ou created_at.
directionnonASC ou DESC.

Exemple de requête (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');

Exemple de réponse

{
  "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
  }
}

Sessions d’intégration et initialisation de l’environnement d’exécution

Créez un jeton d’intégration de courte durée depuis votre serveur, transmettez-le à l’iframe ou à l’environnement d’exécution, puis laissez ce dernier appeler le point d’initialisation avec un jeton Bearer.

ParamètreObligatoireDétails
runtimeBindingIdrecommandéIdentifiant à privilégier pour les associations d’exécution actives.
toolobligatoire sans runtimeBindingIddesigner | viewer | configurator | customizer | model-generator
toolIdtool: model-generatorIdentifiant numérique positif du projet local du générateur, et non son UUID ni l’identifiant du produit WooCommerce.
originouiOrigine sur laquelle l’outil intégré est affiché, par exemple https://yourstore.com.
designIdun identifiantIdentifiant du design Alter Product. Ne pas combiner avec orderId.
orderIdun identifiantIdentifiant de commande Customizer. Valide uniquement pour customizer.
cartKey + cartModenonContexte de panier propre à Customizer. cartMode vaut view ou edit.

Exemple de requête (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 });

Remarques

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
  })
});

Exemple de réponse

{
  "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
}

Générateur de modèles 3D

Les points de terminaison d’import du générateur n’acceptent que les requêtes en lecture entre serveurs. Ils nécessitent les en-têtes API habituels, le scope embed:session:create et un abonnement actif. L’autorisation d’import ne crée pas de session d’édition et ne consomme pas son quota mensuel. L’ouverture de l’éditeur utilise le même compteur monthlyEmbedTokenLimit que les autres outils intégrés.

Rechercher et importer des modèles avec générateur

Utilisez /model-generator/models pour lister les modèles disponibles dotés d’un générateur. Le descripteur generator fixe projectId, revision, configurationId, templateRevision, productId et productModel3dId. Suivez son importPath pour récupérer exactement cette révision source. Les manifestes des ressources produit exposent aussi generators et le descripteur generator de chaque modèle. Les gabarits peuvent être importés séparément selon leur configuration et leur révision.

Filtres des catalogues

Point d’accèsDétails
/model-generator/modelsListe des modèles : name (ou q), categoryId, scope (all, own, global), limit (1–50) et offset.
/model-generator/catalogCatalogue des gabarits : generatorType, productId, audience, q, templateKey, configurationId, limit et offset.
/model-generator/projectsListe des projets : configurationId, q, scope (all, own, global), limit et offset. Le proxy public définit scope sur all par défaut.
/model-generator/image-libraries/:kind/assetsBibliothèques de textures et d’arrière-plans : kind vaut texture ou background ; q, category et mapType filtrent les ressources disponibles.

Un import de projet contient document, revision, template et un manifeste files. Chaque fichier fournit un path sous /v1/model-generator/ ; ajoutez /public-api devant ce chemin pour le télécharger depuis Alter Product. Copiez les fichiers nécessaires dans votre propre espace de stockage et remplacez les références sources par des références locales. Importez les mannequins et les bibliothèques de textures via leurs points de terminaison de catalogue ; public-files n’autorise que certains chemins de ressources, et les gabarits restent soumis aux contrôles d’accès de leur configuration et de leur révision.

Exemple de requête (fetch)

// Server-side: uses the alterFetch helper and authHeaders defined above.
const catalog = await alterFetch('/model-generator/models?' + new URLSearchParams({
  scope: 'all', limit: '24', offset: '0'
}));

const selected = catalog.items[0];
if (!selected?.generator) throw new Error('Select an available generator model');

const importPath = selected.generator.importPath;
if (!importPath.startsWith('/v1/model-generator/projects/')) {
  throw new Error('Invalid generator import path');
}
const bundle = await alterFetch(importPath.slice('/v1'.length));

for (const file of bundle.files) {
  if (!file.path.startsWith('/v1/model-generator/')) {
    throw new Error('Invalid generator file path');
  }
  const response = await fetch('https://alterproduct.com/public-api' + file.path, {
    headers: authHeaders,
    redirect: 'error'
  });
  if (!response.ok) throw new Error(`File download failed: ${response.status}`);
  const bytes = new Uint8Array(await response.arrayBuffer());
  // Persist bytes in your local storage; record the mapping from
  // file.sourceHref / file.href to the resulting local file reference.
}
// Persist bundle.document, bundle.template and revision metadata locally.
// Import the related product asset and its textures/mockups as needed:
const product = await alterFetch('/assets/products/' + selected.generator.productId);
const mannequins = await alterFetch('/model-generator/mannequins');
console.log({ product, mannequins });

Session d’édition et stockage local

Créez la session d’édition avec tool: model-generator, un toolId numérique positif identifiant le projet local du générateur (et non son UUID ni l’ID du produit WooCommerce), ainsi que l’origine autorisée de la boutique. Ne transmettez pas designId, orderId, runtimeBindingId ni les champs du panier pour cet outil. L’UUID du projet du générateur est un identifiant distinct. Transmettez le jeton reçu via la négociation iframe ; l’initialisation du runtime renvoie le contexte du générateur avec storageMode: wordpress_local.

// Server-side, after authorizing the merchant's access to this local project.
const localProjectId = 42; // Local generator project ID, not its UUID or WC product ID.
const session = await alterFetch('/embed/session', {
  method: 'POST',
  body: JSON.stringify({
    tool: 'model-generator',
    toolId: localProjectId,
    origin: 'https://yourstore.com'
  })
});

// The iframe receives session.token through ALTER_CUSTOMIZER_SESSION_READY.
// Do not put API credentials or the token in the iframe URL.
const bootstrapResponse = await fetch('https://alterproduct.com/public-api/v1/runtime/bootstrap', {
  headers: { Authorization: `Bearer ${session.token}` }
});
if (!bootstrapResponse.ok) throw new Error('Generator bootstrap failed');
const context = await bootstrapResponse.json();
console.log(context);

Exemple de réponse

{
  "runtimeBindingId": null,
  "designId": null,
  "productId": 42,
  "toolId": 42,
  "runtimeType": "model-generator",
  "storageMode": "wordpress_local",
  "parentOrigin": "https://yourstore.com"
}
// Host page: WordPress returns a numeric toolId and a UUID in id.
const url = new URL('https://alterproduct.com/app/model-generator');
url.search = new URLSearchParams({
  embedded: '1',
  lng: 'en',
  parentOrigin: window.location.origin,
  toolId: String(localProject.toolId),
  serverProjectId: localProject.id
}).toString();
// Optional: serverProjectRevision pins an existing saved revision.
iframe.src = url.toString();
// Install the authenticated handshake and storage bridge described below.
// Setting iframe.src alone does not authorize the editor or provide storage.

Messages iframe utilisés par la passerelle WordPress

L’extension WordPress héberge la passerelle de stockage et vérifie les droits d’administrateur ou de gestionnaire WooCommerce. Elle valide l’origine de l’iframe, la fenêtre source, le nonce, l’identifiant de requête et les chemins de projet autorisés. La passerelle transmet les lectures et écritures locales à /wp-json/alter-wc/v1/model-generator. Les identifiants API restent sur le serveur. Une intégration personnalisée doit fournir un traitement du stockage authentifié équivalent ; l’API publique du générateur n’enregistre pas les projets sur Alter Product.

TypeDescription
ALTER_CHILD_HELLO / ALTER_PARENT_ACKL’iframe commence la négociation avec un nonce ; la page parente confirme ce même nonce.
ALTER_CUSTOMIZER_INIT_SESSION / ALTER_CUSTOMIZER_SESSION_READYL’iframe demande une session d’édition model-generator ; la page parente renvoie le jeton autorisé.
ALTER_MODEL_GENERATOR_REQUESTL’iframe envoie requestId, nonce et request contenant method, path, data et responseType.
ALTER_MODEL_GENERATOR_RESPONSELa page parente répond avec les mêmes requestId et nonce, ainsi que status, data, headers et, le cas échéant, error.
// Messages after ALTER_CHILD_HELLO / ALTER_PARENT_ACK agree on the nonce.
// Iframe -> parent:
const sessionRequest = {
  type: 'ALTER_CUSTOMIZER_INIT_SESSION',
  nonce: handshakeNonce,
  payload: {
    tool: 'model-generator', alterProductId: localProject.toolId, mode: 'edit'
  }
};

// Parent -> iframe, after the server authorizes the merchant and issues a token:
const sessionReady = {
  type: 'ALTER_CUSTOMIZER_SESSION_READY',
  nonce: handshakeNonce,
  token: session.token,
  tool: 'model-generator',
  cartKey: `model-generator:${localProject.id}`,
  mode: 'edit',
  localSession: false,
  adminSession: true
};
// Send only to the validated iframe's exact origin and source window.
// An authorized token and successful bootstrap are still required.

Un enregistrement transmet expectedRevision, templateRevision, document et les références aux artefacts. Il crée une révision immuable ; une valeur expectedRevision obsolète renvoie HTTP 409. WordPress stocke les métadonnées dans sa base de données et les fichiers dans son répertoire uploads. Le JSON est minifié et compressé avec gzip lorsque la compression réduit sa taille.

// Example message from the iframe; savedSnapshot and artifact IDs come
// from the generator. The host checks origin/source/nonce/project permissions.
const message = {
  type: 'ALTER_MODEL_GENERATOR_REQUEST',
  requestId: crypto.randomUUID(),
  nonce: handshakeNonce,
  request: {
    method: 'POST',
    path: `/pattern-generator/projects/${projectUuid}/revisions`,
    data: {
      expectedRevision: loadedRevision,
      name: projectName,
      templateRevision,
      document: savedSnapshot,
      references: { artifactIds: savedArtifactIds }
    },
    responseType: 'json'
  }
};

Utiliser le modèle enregistré dans le design publie une révision enregistrée complète dans un design produit lié. Les clients la voient ensuite via les outils Customizer, Configurator ou Viewer existants, avec les associations produit et les contrôles d’abonnement habituels. L’éditeur du générateur reste un outil réservé au marchand. Les liens des commandes conservent le projet et la révision enregistrés afin que les modifications ultérieures ne changent pas discrètement les commandes passées.

Catalogue de ressources

Le catalogue de ressources expose les ressources sources des produits, les arrière-plans, les environnements, les éléments de la bibliothèque graphique, les modèles de design et les ressources des maquettes. Les points d’accès de liste renvoient des descripteurs légers ; ceux de détail incluent les manifestes des fichiers.

TypeDescription
productsRessources de produits de base, aperçus, modèles 3D, descripteurs de matériaux et de textures.
backgroundsArrière-plans statiques de Viewer.
environmentsCartes d’environnement et images d’aperçu.
image_libraryRessources de la bibliothèque graphique, y compris les images propres à la boutique.
design_templatesAperçus des modèles de design et références des fichiers de calques. Prend en charge le filtre product_id.
mockupsRessources du générateur de maquettes, arrière-plans et cartes de superposition. Prend en charge le filtre product_id.

Exemple de requête (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();

Exemple de réponse

{
  "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
}

Imports de designs

Les imports de designs donnent accès aux designs hébergés chez Alter et à leurs fichiers pour les processus de production ou de migration externes. L’API vérifie l’éligibilité à l’offre Business.

ParamètreObligatoireDétails
searchnonRecherche le titre ou l’identifiant du design.
offsetnonValeur par défaut : 0.
limitnonValeur par défaut : 20, maximum : 100.

Exemple de requête (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();

Exemple de réponse

{
  "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
}

Fichiers, polices et devises

Les fichiers d’aperçu publics sont directement utilisables dans le navigateur. Les fichiers protégés nécessitent une URL signée ou un identifiant API doté de la portée files:read. Les polices et les devises sont accessibles via des points de lecture publics.

Point d’accèsAccèsDétails
/file/public/products/:productId/small.pngpublicPetit aperçu du produit.
/file/public/products/:productId/medium.pngpublicAperçu moyen du produit.
/file/public/products/:productId/big.pngpublicGrand aperçu du produit.
/file/protected/:keyURL signée ou files:readFichier protégé du stockage d’objets.
/fontspublicTableau d’enregistrements de polices.
/currenciespublicTableau d’enregistrements de devises.

Exemple de requête (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());

Exemple de réponse

[
  {
    "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
  }
]

Associations des environnements d’exécution

Les associations d’exécution relient les produits commerciaux externes aux designs Alter Product et aux types d’outils. Elles sont principalement utilisées par les intégrations WordPress/WooCommerce et les systèmes e-commerce avancés.

ParamètreObligatoireDétails
designIdnonIdentifiant du design Alter Product appartenant à la boutique.
externalProductIdoui pour la synchronisationIdentifiant du produit externe, par exemple celui d’un produit WooCommerce.
runtimeTypeoui pour la synchronisationviewer, configurator ou customizer.
statusnondraft, active, inactive, archived ou legacy_active.
legacyStorefrontProductIdnonIdentifiant d’association historique facultatif.
legacyBindingMetanonMétadonnées JSON facultatives, par exemple manifestHash.

Exemple de requête (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'
          }
        }
      }
    ]
  })
});

Exemple de réponse

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

Échange de connexion WordPress

Le point d’accès d’échange de connexion WordPress consomme un code de transfert à usage unique et renvoie des identifiants API à l’extension. Il ne s’agit pas d’un point d’accès général de création d’identifiants.

Exemple de requête (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();

Exemple de réponse

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

Erreurs et limites de requêtes

La plupart des erreurs des contrôleurs sont normalisées en réponses code. Les intergiciels d’authentification et les limiteurs de requêtes peuvent renvoyer une réponse error à la place.

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

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

{
  "error": "Too Many Requests"
}
TypeLimitePériode
Global600 requêtes60 secondes
GET /auth/check60 requêtes60 secondes
Lecture des commandes/produits300 requêtes60 secondes
Écriture des commandes/sessions d’intégration/associations d’exécution120 requêtes60 secondes
Lecture des ressources/imports de designs180 requêtes60 secondes
Polices300 requêtes60 secondes
Échange de connexion WP30 requêtes60 secondes
GET /model-generator/*600 requêtes60 secondes