Integration of 1C-Bitrix with Salsify PIM System
Salsify is an American SaaS PIM platform focused on large e-commerce and multi-channel sales. It is used primarily by brands and distributors managing thousands of SKUs with syndication to marketplaces (Amazon, Walmart, Target). If a Western brand has been migrated to Bitrix or a partnership requires using Salsify as a data source — integration is built through the Salsify REST API.
Salsify API: Key Endpoints
Salsify provides REST API with token authentication:
Base URL: https://app.salsify.com/api/v1/orgs/{org_id}/
Authorization: Bearer {api_key}
Key endpoints:
GET products — list of products with attributes
GET products/{product_id} — specific product
GET products?filter[updated_at][gte]={timestamp} — changed since date
GET property_groups — attribute groups (similar to property sections)
GET assets?filter[product_id]={id} — product media files
GET digital_assets/{id}/download — download file
Salsify uses a flat attribute structure: each attribute is a name:value pair where name can be an arbitrary UUID or string. Before mapping, retrieve the attribute reference:
GET properties — returns all organization attributes with ID, name, data_type
Salsify Client
class SalsifyClient
{
private string $baseUrl;
private string $apiKey;
public function __construct(string $orgId, string $apiKey)
{
$this->baseUrl = "https://app.salsify.com/api/v1/orgs/{$orgId}/";
$this->apiKey = $apiKey;
}
private function request(string $endpoint, array $params = []): array
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
$http->setHeader('Content-Type', 'application/json');
$url = $this->baseUrl . $endpoint;
if ($params) {
$url .= '?' . http_build_query($params);
}
$response = $http->get($url);
return json_decode($response, true) ?? [];
}
public function getProducts(int $page = 1, int $perPage = 100, ?string $updatedAfter = null): array
{
$params = ['page' => $page, 'per_page' => $perPage];
if ($updatedAfter) {
$params['filter[updated_at][gte]'] = $updatedAfter;
}
return $this->request('products', $params);
}
public function getProperties(): array
{
return $this->request('properties');
}
public function downloadAsset(string $assetId): string
{
$http = new \Bitrix\Main\Web\HttpClient();
$http->setHeader('Authorization', 'Bearer ' . $this->apiKey);
return $http->get($this->baseUrl . 'digital_assets/' . $assetId . '/download');
}
}
Salsify Attribute Mapping
Salsify attributes are identified by salsify:id (UUID) or by name. Mapping configuration:
// /local/config/salsify-mapping.php
// 'salsify_property_id_or_name' => ['target' => 'bitrix_field', 'type' => 'field|prop']
return [
'Product Name' => ['target' => 'NAME', 'type' => 'field'],
'Long Description' => ['target' => 'DETAIL_TEXT', 'type' => 'field'],
'Short Description' => ['target' => 'PREVIEW_TEXT', 'type' => 'field'],
'Brand' => ['target' => 'BRAND', 'type' => 'prop'],
'Net Weight (kg)' => ['target' => 'WEIGHT', 'type' => 'prop'],
'Color' => ['target' => 'COLOR', 'type' => 'prop'],
'Country of Origin' => ['target' => 'COUNTRY_ORIGIN','type' => 'prop'],
'GTIN' => ['target' => 'CML2_BAR_CODE', 'type' => 'prop'],
'Manufacturer SKU' => ['target' => 'CML2_ARTICLE', 'type' => 'prop'],
];
Synchronization
function syncSalsifyAgent(): string
{
$lastSync = \Bitrix\Main\Config\Option::get('salsify_sync', 'last_run', '');
$client = new SalsifyClient(SALSIFY_ORG_ID, SALSIFY_API_KEY);
$mapping = include '/local/config/salsify-mapping.php';
$page = 1;
$newSync = date('c');
do {
$response = $client->getProducts($page, 100, $lastSync ?: null);
$products = $response['products'] ?? [];
foreach ($products as $product) {
importSalsifyProduct($product, $mapping, $client);
}
$page++;
$meta = $response['meta'] ?? [];
} while (($meta['current_page'] ?? 1) < ($meta['total_pages'] ?? 1));
\Bitrix\Main\Config\Option::set('salsify_sync', 'last_run', $newSync);
return __FUNCTION__ . '();';
}
function importSalsifyProduct(array $product, array $mapping, SalsifyClient $client): void
{
$attributes = $product['attributes'] ?? [];
$sku = $product['salsify:id'];
// Get attribute values
$getValue = static function (array $attrs, string $key): mixed {
return $attrs[$key] ?? null;
};
$fields = ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'ACTIVE' => 'Y'];
$props = [];
foreach ($mapping as $salsifyKey => $config) {
$value = $getValue($attributes, $salsifyKey);
if ($value === null) continue;
// Convert arrays to string via separator
if (is_array($value)) {
$value = implode(', ', $value);
}
if ($config['type'] === 'field') {
$fields[$config['target']] = $value;
} else {
$props[$config['target']] = $value;
}
}
$existing = CIBlockElement::GetList(
[], ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
)->Fetch();
$el = new CIBlockElement();
if ($existing) {
$productId = $existing['ID'];
$el->Update($productId, $fields);
} else {
$productId = $el->Add($fields);
}
if ($productId) {
CIBlockElement::SetPropertyValuesEx($productId, CATALOG_IBLOCK_ID, $props);
// Media files
$primaryImage = $product['salsify:primary_image'] ?? null;
if ($primaryImage) {
importSalsifyAsset($productId, $primaryImage, $client);
}
}
}
Salsify Specifics
Complex attribute values. Salsify returns attributes as an array where each element can be a scalar value, object, or list. Enum attributes return {"salsify:id": "...", "salsify:name": "Red"} — need to extract salsify:name.
Prices. Salsify is not a pricing system. Prices are typically not synchronized from Salsify — they come from 1C. Check with the client whether Salsify is a price source or only content.
Media files. Salsify stores media files with CDN links. You can avoid downloading files and store external URLs in the EXTERNAL_IMAGE_URL property, pulling them on the fly via <img src="...">. This saves server space for Bitrix.
Webhook. Salsify supports webhooks when products change. Configure in Salsify → Integrations → Webhooks. The handler URL receives a POST request with product_id, then requests current data via API.
Implementation Timeline
| Scope | Components | Timeline |
|---|---|---|
| Up to 5,000 SKU, basic content | Client + mapping + agent | 1–2 weeks |
| 10,000–100,000 SKU + media + webhooks | + processing complex attributes + optimization | 3–4 weeks |
| Multi-regional catalog (different prices, content by region) | + Salsify channels logic + Bitrix multisite | 5–7 weeks |







