OpenAkeneo.RestApiClient 0.5.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package OpenAkeneo.RestApiClient --version 0.5.0
                    
NuGet\Install-Package OpenAkeneo.RestApiClient -Version 0.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenAkeneo.RestApiClient" Version="0.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenAkeneo.RestApiClient" Version="0.5.0" />
                    
Directory.Packages.props
<PackageReference Include="OpenAkeneo.RestApiClient" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpenAkeneo.RestApiClient --version 0.5.0
                    
#r "nuget: OpenAkeneo.RestApiClient, 0.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpenAkeneo.RestApiClient@0.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpenAkeneo.RestApiClient&version=0.5.0
                    
Install as a Cake Addin
#tool nuget:?package=OpenAkeneo.RestApiClient&version=0.5.0
                    
Install as a Cake Tool

OpenAkeneo.RestApiClient

NuGet NuGet Downloads Build License: MIT

Unofficial .NET client library for the Akeneo PIM REST API — typed models, automatic OAuth token management, streaming pagination, and Polly-based retry handling.

Note: This project is currently in development, APIs may change between minor versions until 1.0. Use at your own risk!

Note: Akeneo is a registered trademark of Akeneo SA. This project is not affiliated with Akeneo SA.

Note: This project was developed with assistance from AI tools. Please consider this fact against your AI governance policy before using this library.


Why this exists

Akeneo does not publish an official .NET SDK. The options available are either outdated, incomplete, or tightly coupled to specific project structures. This library was built to fill that gap — a clean, modern .NET client that handles the OAuth lifecycle, retries, and HAL pagination so you can focus on working with your PIM data rather than the HTTP layer.


Features

  • Two-layer architecture: low-level HTTP service + high-level typed context
  • Automatic OAuth token acquisition, in-memory caching, and proactive refresh at 75% of token lifetime
  • Transparent 401 retry — fetches a new token and retries once without any extra code
  • IAsyncEnumerable<T> streaming for all list resources — handles pagination automatically
  • Polly resilience pipeline: 5 retries with exponential back-off, jitter, and Retry-After support
  • CancellationToken support on every method
  • Optional disk-based token cache for persistence across process restarts

AI & LLM Integration

Are you using an AI coding assistant (like GitHub Copilot, Cursor, or Claude) to help you build with OpenAkeneo.RestApiClient? We have prepared highly optimized, compact markdown representations of the Akeneo REST API specifically designed to be read by LLMs.

You can provide the following RAW links directly to your AI assistant to give it full context on the API endpoints, methods, and request/response schemas available:

Prompt Example:

"Read the API index at https://raw.githubusercontent.com/OpenAkeneo/dotnet-rest-api-client/main/OpenAkeneo.RestApiClient.ApiIndex.md to find the endpoints I need to update a product, then read the relevant lines from the API Reference at https://raw.githubusercontent.com/OpenAkeneo/dotnet-rest-api-client/main/OpenAkeneo.RestApiClient.ApiReference.md and write the C# code using OpenAkeneo.RestApiClient."


Installation

dotnet add package OpenAkeneo.RestApiClient

Or search for OpenAkeneo.RestApiClient in the NuGet Package Manager.


Quickstart

// 1. Load settings
var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: false)
    .Build();

var settings = configuration.GetSection("AkeneoSettings").Get<AkeneoRestApiSettings>();

// 2. Create the client
var context = new AkeneoContext(settings);

// 3. Use it
await foreach (var product in context.StreamProductUuidsAsync())
{
    Console.WriteLine($"{product.Uuid} — enabled: {product.Enabled}");
}

Configuration

Copy appsettings.example.json to appsettings.json and fill in your credentials:

{
  "AkeneoSettings": {
    "Id": "MyConnection",
    "Name": "My Akeneo Connection",
    "ClientId": "your_client_id",
    "ClientSecret": "your_client_secret",
    "Username": "your_api_username",
    "Password": "your_api_password",
    "RestApiUrl": "https://your-instance.cloud.akeneo.com",
    "TokenFilePath": ""
  }
}

TokenFilePath is optional. When set (e.g. "akeneo_token.{0}.json"), the OAuth token is cached to disk and reused across restarts. Leave empty to cache in memory only.

Security note: TokenFilePath should point to a directory with restrictive ACLs. Never place it on a shared filesystem or in a web-accessible path.


Architecture

Layer Class Responsibility
Low-level AkeneoRestApiService OAuth, HTTP, retries, token cache
High-level AkeneoContext Typed methods for all Akeneo resources

For most use cases, only AkeneoContext is needed.


Dependency Injection (ASP.NET Core / hosted services)

For long-running applications, use AddAkeneoClient so IHttpClientFactory manages handler rotation, preventing stale-DNS and socket exhaustion:

// Program.cs
var settings = builder.Configuration.GetSection("AkeneoSettings").Get<AkeneoRestApiSettings>();

builder.Services.AddAkeneoClient(settings);

Then inject AkeneoContext into your services:

public class ProductSyncService
{
    private readonly AkeneoContext _akeneo;

    public ProductSyncService(AkeneoContext akeneo)
    {
        _akeneo = akeneo;
    }

    public async Task SyncAsync(CancellationToken ct)
    {
        await foreach (var product in _akeneo.StreamProductUuidsAsync(ct: ct))
        {
            // process product
        }
    }
}

For scripts and console apps where DI is not used, new AkeneoContext(settings) is the simpler option.


Usage

Token Management

Tokens are acquired automatically on the first call. Explicit control is also available:

var token = await context.Service.GetTokenAsync();
var freshToken = await context.Service.GetTokenAsync(forceRefresh: true);

Tokens are refreshed automatically at 75% of their lifetime. On a 401 Unauthorized response the service transparently fetches a new token and retries once.


Error Handling

All API errors throw AkeneoApiException:

try
{
    var product = await context.GetProductUuidAsync("non-existent-uuid");
}
catch (AkeneoApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
    Console.WriteLine($"Product not found: {ex.ApiMessage}");
}
catch (AkeneoApiException ex)
{
    Console.WriteLine($"API error {ex.StatusCode} at {ex.RequestUrl}: {ex.ApiMessage}");
}

Pagination Patterns

Every list resource exposes three access patterns:

Method Description
StreamXxxAsync() IAsyncEnumerable<T> — yields items one-by-one, fetches pages on demand. Best for large catalogs.
GetXxxListAsync(page, limit) Returns a single page with HAL navigation links.
GetXxxListFullAsync() Buffers all pages into a List<T>. Convenient but may use significant memory on large catalogs.

Search Filter Syntax

Many list methods accept a search parameter. The value is a JSON-encoded Akeneo search filter following the Akeneo filter syntax:

// Products enabled in the ecommerce channel
var search = """{"enabled":[{"operator":"=","value":true}]}""";

// Products updated since a date
var search = """{"updated":[{"operator":">","value":"2024-01-01 00:00:00"}]}""";

// Products in a specific category (including children)
var search = """{"categories":[{"operator":"IN CHILDREN","value":["master"]}]}""";

// Attributes of a specific type
var search = """{"type":[{"operator":"IN","value":["pim_catalog_simpleselect","pim_catalog_multiselect"]}]}""";

The search value must be URL-safe JSON — the library handles encoding automatically.


System Information

var info = await context.GetSystemInformationAsync();
Console.WriteLine($"Akeneo version: {info.Version}, edition: {info.Edition}");

Products (UUID-based)

The preferred modern API uses product UUIDs.

// Stream all products
await foreach (var product in context.StreamProductUuidsAsync())
    Console.WriteLine($"{product.Uuid} — enabled: {product.Enabled}");

// Stream with search filter, scoped to a channel with specific locales
var search = """{"enabled":[{"operator":"=","value":true}]}""";
await foreach (var product in context.StreamProductUuidsAsync(
    search: search,
    scope: "ecommerce",
    locales: "en_US,fr_FR"))
{
    Console.WriteLine(product.Uuid);
}

// Include CDN share links for asset_collection attribute values
await foreach (var product in context.StreamProductUuidsAsync(withAssetShareLinks: true))
{
    // product.Values["my_asset_collection"][0].GetLinkedData<Dictionary<string, AssetCollectionLinkedDataEntry>>()
}

// Single product by UUID
var product = await context.GetProductUuidAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

// Create or update (PATCH)
await context.CreateOrUpdateProductUuidAsync(new ProductUuid
{
    Uuid = "a4f47e32-b29c-4f3d-a0b2-123456789abc",
    Enabled = true,
    Family = "clothing"
});

// Create (POST — returns the created product)
var created = await context.CreateProductUuidAsync(new ProductUuid
{
    Family = "clothing",
    Enabled = true
});

// Delete
await context.DeleteProductUuidAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

// Submit for approval (requires Workflow feature)
await context.SubmitProductUuidProposalAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

// Search product UUIDs (returns the matching UUIDs list resource)
var search = """{"enabled":[{"operator":"=","value":true}]}""";
var uuids = await context.SearchProductUuidsAsync(search: search);

// Upload a media file and get back the file code
var fileBytes = await File.ReadAllBytesAsync("image.jpg");
var fileCode = await context.UploadProductMediaFileAsync(fileBytes, "image.jpg", "image/jpeg");

// Draft (requires Workflow feature)
var draft = await context.GetProductUuidDraftAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

Products (Identifier-based)

The legacy identifier (SKU) API is also fully supported.

await foreach (var product in context.StreamProductIdentifiersAsync())
    Console.WriteLine(product.Identifier);

var product = await context.GetProductIdentifierAsync("my-sku-001");

await context.CreateOrUpdateProductIdentifierAsync(new ProductIdentifier
{
    Identifier = "my-sku-001",
    Family = "clothing",
    Enabled = true
});

// Create (POST)
var created = await context.CreateProductIdentifierAsync(new ProductIdentifier
{
    Identifier = "new-sku-002",
    Family = "clothing"
});

// Delete
await context.DeleteProductIdentifierAsync("my-sku-001");

// Submit for approval (requires Workflow feature)
await context.SubmitProductIdentifierProposalAsync("my-sku-001");

var draft = await context.GetProductIdentifierDraftAsync("my-sku-001");

Reading Product Attribute Values

Product attribute values live in product.Values, keyed by attribute code. Each entry is a list of ProductValue objects (one per locale/scope combination).

var product = await context.GetProductUuidAsync("a4f47e32-b29c-4f3d-a0b2-123456789abc");

if (product.Values != null && product.Values.TryGetValue("name", out var nameValues))
{
    // Get the en_US value (non-scopable attribute)
    var enValue = nameValues.FirstOrDefault(v => v.Locale == "en_US");
    Console.WriteLine(enValue?.GetStringData()); // returns string directly
}

// Numeric attribute
if (product.Values.TryGetValue("weight", out var weightValues))
{
    var metric = weightValues.FirstOrDefault(v => v.Locale == null && v.Scope == null)
                             ?.GetData<MetricValue>();
    Console.WriteLine($"{metric?.Amount} {metric?.Unit}"); // e.g. "1.5 KILOGRAM"
}

// Boolean attribute
if (product.Values.TryGetValue("is_new", out var isNewValues))
{
    bool? isNew = isNewValues.FirstOrDefault()?.GetData<bool>();
}

// Multiselect attribute (list of option codes)
if (product.Values.TryGetValue("color", out var colorValues))
{
    var codes = colorValues.FirstOrDefault()?.GetData<List<string>>();
}

// Price collection
if (product.Values.TryGetValue("price", out var priceValues))
{
    var prices = priceValues.FirstOrDefault()
                            ?.GetData<List<Dictionary<string, object?>>>();
    // Each dict has "amount" and "currency" keys
}

// Asset collection — get CDN share links (requires withAssetShareLinks: true)
if (product.Values.TryGetValue("packshots", out var assetValues))
{
    var links = assetValues.FirstOrDefault()
        ?.GetLinkedData<Dictionary<string, AssetCollectionLinkedDataEntry>>();

    foreach (var (assetCode, entry) in links ?? [])
    foreach (var link in entry.ShareLinks ?? [])
        Console.WriteLine($"{assetCode}: {link.Links?.Self?.Href}");
}

GetStringData() covers text, textarea, identifier, date, file, image, and simpleselect attributes. For all other types use GetData<T>() with the appropriate type.


Product Models

await foreach (var model in context.StreamProductModelsAsync())
    Console.WriteLine($"{model.Code} — family: {model.Family}");

var model = await context.GetProductModelAsync("summer_collection_2024");

await context.CreateOrUpdateProductModelAsync(new ProductModel
{
    Code = "summer_collection_2024",
    Family = "clothing",
    FamilyVariant = "clothing_color_size"
});

// Create (POST)
var created = await context.CreateProductModelAsync(new ProductModel
{
    Code = "winter_2025",
    Family = "clothing",
    FamilyVariant = "clothing_color_size"
});

// Delete
await context.DeleteProductModelAsync("summer_collection_2024");

// Submit for approval (requires Workflow feature)
await context.SubmitProductModelProposalAsync("summer_collection_2024");

var draft = await context.GetProductModelDraftAsync("summer_collection_2024");

Product Media Files

await foreach (var file in context.StreamProductMediaFilesAsync())
    Console.WriteLine($"{file.Code} — {file.MimeType}");

var fileMeta = await context.GetProductMediaFileAsync("f/f/c/f/ffcf2...125_image.jpg");

byte[] bytes = await context.DownloadProductMediaFileAsync("f/f/c/f/ffcf2...125_image.jpg");
File.WriteAllBytes("output.jpg", bytes);

Families & Variants

await foreach (var family in context.StreamFamiliesAsync())
    Console.WriteLine(family.Code);

var family = await context.GetFamilyAsync("digital_cameras");

await context.CreateOrUpdateFamilyAsync(new Family
{
    Code = "my_family",
    AttributeAsLabel = "name",
    Labels = new() { ["en_US"] = "My Family" }
});

// Create (POST)
var createdFamily = await context.CreateFamilyAsync(new Family
{
    Code = "accessories",
    AttributeAsLabel = "name"
});

await foreach (var variant in context.StreamFamilyVariantsAsync("clothing"))
    Console.WriteLine(variant.Code);

var variant = await context.GetFamilyVariantAsync("clothing", "clothing_color_size");

await context.CreateOrUpdateFamilyVariantAsync("clothing", new FamilyVariant
{
    Code = "clothing_color_size",
    Labels = new() { ["en_US"] = "Color and size" }
});

// Create variant (POST)
var createdVariant = await context.CreateFamilyVariantAsync("clothing", new FamilyVariant
{
    Code = "clothing_size_only",
    Labels = new() { ["en_US"] = "Size only" }
});

Attributes

await foreach (var attr in context.StreamAttributesAsync())
    Console.WriteLine($"{attr.Code} — {attr.Type}");

// With a type filter
var search = """{"type":[{"operator":"IN","value":["pim_catalog_simpleselect"]}]}""";
var selectAttrs = await context.GetAttributeListFullAsync(search: search);

// Include table select options (for pim_catalog_table attributes)
var tableAttrs = await context.GetAttributeListFullAsync(withTableSelectOptions: true);

var attr = await context.GetAttributeAsync("accessories_care_instructions");

await context.CreateOrUpdateAttributeAsync(new AkeneoAttribute
{
    Code = "my_text_attr",
    Type = "pim_catalog_text",
    Group = "general"
});

// Create (POST)
var created = await context.CreateAttributeAsync(new AkeneoAttribute
{
    Code = "launch_date",
    Type = "pim_catalog_date",
    Group = "marketing"
});

Attribute Options & Groups

// Options
await foreach (var option in context.StreamAttributeOptionsAsync("color"))
    Console.WriteLine($"{option.Code}");

var options = await context.GetAttributeOptionListFullAsync("color");
var option  = await context.GetAttributeOptionAsync("color", "red");

await context.CreateOrUpdateAttributeOptionAsync("color", new AttributeOption
{
    Code = "navy",
    Labels = new() { ["en_US"] = "Navy Blue" }
});

// Create option (POST)
var createdOption = await context.CreateAttributeOptionAsync("color", new AttributeOption
{
    Code = "teal",
    Labels = new() { ["en_US"] = "Teal" }
});

// Groups
await foreach (var group in context.StreamAttributeGroupsAsync())
    Console.WriteLine(group.Code);

await context.CreateOrUpdateAttributeGroupAsync(new AttributeGroup
{
    Code = "marketing",
    Labels = new() { ["en_US"] = "Marketing" }
});

// Create group (POST)
var createdGroup = await context.CreateAttributeGroupAsync(new AttributeGroup
{
    Code = "technical",
    Labels = new() { ["en_US"] = "Technical" }
});

Categories

await foreach (var cat in context.StreamCategoriesAsync())
    Console.WriteLine($"{cat.Code} — parent: {cat.Parent}");

// Include enriched attributes (category media, labels)
await foreach (var cat in context.StreamCategoriesAsync(withEnrichedAttributes: true))
    Console.WriteLine(cat.Code);

var cat = await context.GetCategoryAsync("master");

await context.CreateOrUpdateCategoryAsync(new Category
{
    Code = "sale",
    Parent = "master",
    Labels = new() { ["en_US"] = "Sale" }
});

// Create (POST)
var created = await context.CreateCategoryAsync(new Category
{
    Code = "new_arrivals",
    Parent = "master",
    Labels = new() { ["en_US"] = "New Arrivals" }
});

// Upload a category media file
var imageBytes = await File.ReadAllBytesAsync("banner.png");
var fileCode = await context.UploadCategoryMediaFileAsync(imageBytes, "banner.png", "image/png");

byte[] bytes = await context.DownloadCategoryMediaFileAsync("c/7/3/c/c73cc4...ecf4_banner.png");

Channels, Locales, Currencies, Measurement Families

// Channels
var channels = await context.GetChannelListFullAsync();
var channel  = await context.GetChannelAsync("ecommerce");
await context.CreateOrUpdateChannelAsync(new Channel { Code = "b2b", /* ... */ });

// Create channel (POST)
var created = await context.CreateChannelAsync(new Channel
{
    Code = "mobile",
    Locales = ["en_US"],
    Currencies = ["USD"]
});

// Locales
var locales = await context.GetLocaleListFullAsync();
var locale  = await context.GetLocaleAsync("en_US");

// Currencies
var currencies = await context.GetCurrencyListFullAsync();
var currency   = await context.GetCurrencyAsync("USD");

// Measurement families (no paging — returns all at once)
var measurementFamilies = await context.GetMeasurementFamilyListAsync();

// Create or update measurement families (PATCH — returns raw response JSON)
var responseJson = await context.CreateOrUpdateMeasurementFamiliesAsync(new List<MeasurementFamily>
{
    new() { Code = "CUSTOM_WEIGHT", StandardUnitCode = "GRAM" }
});

Association Types

var all   = await context.GetAssociationTypeListFullAsync();
var assoc = await context.GetAssociationTypeAsync("x_sell");

await context.CreateOrUpdateAssociationTypeAsync(new AssociationType
{
    Code = "bundle",
    Labels = new() { ["en_US"] = "Bundle" }
});

// Create (POST)
var created = await context.CreateAssociationTypeAsync(new AssociationType
{
    Code = "related",
    Labels = new() { ["en_US"] = "Related" }
});

Reference Entities

// Entity definitions
var entities = await context.GetReferenceEntityListFullAsync();
var entity   = await context.GetReferenceEntityAsync("brand");
await context.CreateOrUpdateReferenceEntityAsync(new ReferenceEntity { Code = "brand" });

// Entity attributes
var attributes = await context.GetReferenceEntityAttributeListAsync("brand");
var attribute  = await context.GetReferenceEntityAttributeAsync("brand", "description");
await context.CreateOrUpdateReferenceEntityAttributeAsync("brand", new ReferenceEntityAttribute
{
    Code = "description",
    Type = "text"
});

// Attribute options
var options = await context.GetReferenceEntityAttributeOptionListAsync("brand", "country");
await context.CreateOrUpdateReferenceEntityAttributeOptionAsync("brand", "country",
    new ReferenceEntityAttributeOption { Code = "se", Labels = new() { ["en_US"] = "Sweden" } });

// Records
await foreach (var record in context.StreamReferenceEntityRecordsAsync("brand"))
    Console.WriteLine(record.Code);

var records = await context.GetReferenceEntityRecordListFullAsync("brand");
var record  = await context.GetReferenceEntityRecordAsync("brand", "nike");
await context.CreateOrUpdateReferenceEntityRecordAsync("brand", new ReferenceEntityRecord
{
    Code = "acme",
    Values = new() { ["name"] = new() { new ReferenceEntityRecordValue { Data = "Acme Corp", Locale = "en_US" } } }
});

// Media files
byte[] bytes = await context.DownloadReferenceEntityMediaFileAsync("f/f/c/f/...logo.png");

// Upload a reference entity media file
var fileBytes = await File.ReadAllBytesAsync("logo.png");
var fileCode = await context.UploadReferenceEntityMediaFileAsync(fileBytes, "logo.png", "image/png");

Assets

// Asset families
var families  = await context.GetAssetFamilyListFullAsync();
var family    = await context.GetAssetFamilyAsync("packshots");
await context.CreateOrUpdateAssetFamilyAsync(new AssetFamily { Code = "packshots" });

// Asset family attributes
var attrs = await context.GetAssetAttributeListAsync("packshots");
var attr  = await context.GetAssetAttributeAsync("packshots", "media_file");
await context.CreateOrUpdateAssetAttributeAsync("packshots", new AssetAttribute
{
    Code = "alt_text",
    Type = "text"
});

// Asset attribute options
var options = await context.GetAssetAttributeOptionListAsync("packshots", "orientation");
await context.CreateOrUpdateAssetAttributeOptionAsync("packshots", "orientation",
    new AssetAttributeOption { Code = "landscape" });

// Assets within a family
await foreach (var asset in context.StreamAssetsAsync("packshots"))
    Console.WriteLine(asset.Code);

var asset = await context.GetAssetAsync("packshots", "front_view");
await context.CreateOrUpdateAssetAsync("packshots", new Asset { Code = "front_view" });

// Download asset binary
byte[] bytes = await context.DownloadAssetMediaFileAsync("path/to/asset_file.jpg");

// Upload an asset media file
var fileBytes = await File.ReadAllBytesAsync("photo.jpg");
var fileCode = await context.UploadAssetMediaFileAsync(fileBytes, "photo.jpg", "image/jpeg");

Jobs

// List available jobs
var jobs = await context.GetJobListFullAsync();
var job  = await context.GetJobAsync("csv_product_export");

// Launch export
var result = await context.LaunchExportJobAsync("csv_product_export");
Console.WriteLine($"Launched job execution #{result.JobExecutionId}");

// Dry run
var dryRun = await context.LaunchExportJobAsync("csv_product_export", isDryRun: true);

// Launch import
var importResult = await context.LaunchImportJobAsync("csv_product_import");

// Poll execution status
var execution = await context.GetJobExecutionAsync(result.JobExecutionId);
Console.WriteLine($"Status: {execution.Status}");

// Browse execution history
await foreach (var exec in context.StreamJobExecutionsAsync())
    Console.WriteLine($"{exec.JobLabel} — {exec.Status}");

Workflows

// Workflow definitions
await foreach (var workflow in context.StreamWorkflowsAsync())
    Console.WriteLine(workflow.Code);

var workflow = await context.GetWorkflowAsync("product_review");

// Workflow tasks (items awaiting action)
await foreach (var task in context.StreamWorkflowTasksAsync())
    Console.WriteLine($"{task.Uuid} — {task.Status}");

// Include attribute values on tasks
var tasks = await context.GetWorkflowTaskListFullAsync(withAttributes: true);

var task = await context.GetWorkflowTaskAsync("task-uuid-here");

// Step assignees
var assignees = await context.GetWorkflowStepAssigneeListFullAsync("step-uuid-here");

Catalogs

// List catalogs
await foreach (var catalog in context.StreamCatalogsAsync())
    Console.WriteLine($"{catalog.Id} — {catalog.Name}");

var catalog = await context.GetCatalogAsync("my-catalog-id");

// Stream all product UUIDs from a catalog
await foreach (var uuid in context.StreamCatalogProductUuidsAsync("my-catalog-id"))
    Console.WriteLine(uuid);

// Stream full product objects from a catalog
await foreach (var product in context.StreamCatalogProductsAsync("my-catalog-id"))
    Console.WriteLine(product.Uuid);

// Single product from catalog
var product = await context.GetCatalogProductAsync("my-catalog-id", "product-uuid");

// Mapped products (returns raw JSON string — use when catalog has a product mapping configured)
var mappedJson   = await context.GetCatalogMappedProductListAsync("my-catalog-id");
var mappedModels = await context.GetCatalogMappedModelListAsync("my-catalog-id");
var mappedVars   = await context.GetCatalogMappedVariantListAsync("my-catalog-id");

// Mapping schema
var schema = await context.GetCatalogMappingSchemaAsync("my-catalog-id");

// Create catalog (POST)
var created = await context.CreateCatalogAsync(new Catalog { Name = "B2B Catalog" });

// Update catalog (PATCH — returns updated catalog)
var updated = await context.UpdateCatalogAsync("my-catalog-id", new Catalog { Name = "Renamed" });

// Duplicate catalog
var copy = await context.DuplicateCatalogAsync("my-catalog-id");

// Set or delete mapping schema
var schemaJson = """{"properties":{"uuid":{"type":"string"}}}""";
await context.SetCatalogMappingSchemaAsync("my-catalog-id", schemaJson);
await context.DeleteCatalogMappingSchemaAsync("my-catalog-id");

// Delete catalog
await context.DeleteCatalogAsync("my-catalog-id");

Utilities

// System information
var info = await context.GetSystemInformationAsync();
Console.WriteLine($"Akeneo {info.Edition} {info.Version}");

// API overview (available endpoints)
var overview = await context.GetApiOverviewAsync();

// User channel and locale permissions
var channelPerms = await context.GetUserChannelsPermissionsAsync("user-uuid");
var localePerms  = await context.GetUserLocalesPermissionsAsync("user-uuid");

// Extensions
await foreach (var ext in context.StreamExtensionsAsync())
    Console.WriteLine(ext.Code);

// Modelization suggestions
var suggestions = await context.GetModelizationSuggestionListAsync();
var suggestion  = await context.GetModelizationSuggestionAsync("suggestion-uuid");

Cancellation Token Support

Every method accepts an optional CancellationToken:

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));

await foreach (var product in context.StreamProductUuidsAsync(ct: cts.Token))
    Console.WriteLine(product.Uuid);

Resilience and Retries

AkeneoRestApiService uses a Polly resilience pipeline aligned with Akeneo's recommended retry strategy:

  • 5 retries with exponential back-off starting at 500 ms, capped at 30 s, with jitter
  • Handles 429 Too Many Requests, 408 Request Timeout, transient 5xx errors, and network faults
  • Respects the Retry-After response header automatically
  • Logs retries at Warning level when a logger is provided

No additional configuration is required.


API Reference

Full Akeneo REST API documentation: api.akeneo.com


License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.8.0 96 6/10/2026
0.7.0 96 6/5/2026
0.6.0 97 6/4/2026
0.5.0 106 5/21/2026
0.4.0 101 5/17/2026
0.3.1 96 5/16/2026
0.3.0 95 5/15/2026
0.2.0 100 5/15/2026
0.1.0 109 5/8/2026