Public API documentation

Ultra B2B API V1

Public V1 endpoints for partner integrations. The API covers authentication, product catalog data, stock quantities, catalog changes, brands, categories, and orders. All routes use the /api base path.

Basics

Base URL

Use your assigned Ultra host with the API prefix:

https://eshop.ultra.md/api

Headers

Send JSON and include the bearer token on protected endpoints:

Accept: application/json
Content-Type: application/json
Authorization: Bearer <access_token>

X-API-Token: <access_token> is also accepted for protected V1 routes.

Authentication

POST /api/auth/token and POST /api/auth/refresh are public. All other V1 routes in this document require a valid bearer token and use the bearer-api throttle. Access tokens expire after 1 hour. Refresh tokens expire after 30 days and rotate on every refresh.

Create tokens

POST /api/auth/token

Request body:

{
  "username": "dealer@example.com",
  "password": "secret"
}

Response status: 200 OK

{
  "access_token": "1|plain-access-token",
  "refresh_token": "plain-refresh-token",
  "expires_in": 3600,
  "token_type": "Bearer",
  "user_email": "dealer@example.com",
  "user_id": 10
}
Response fieldDescription
access_tokenBearer token used in protected V1 API requests.
refresh_tokenLong-lived token used only to request a new access token.
expires_inAccess token lifetime in seconds.
token_typeAlways Bearer.
user_emailEmail of the authenticated dealer account.
user_idInternal user ID for support and integration diagnostics.

Refresh access token

POST /api/auth/refresh

Request body:

{
  "refresh_token": "plain-refresh-token"
}

Response status: 200 OK

{
  "access_token": "2|new-plain-access-token",
  "refresh_token": "new-plain-refresh-token",
  "expires_in": 3600,
  "token_type": "Bearer"
}

Revoke access token

With an empty payload, the current access token is revoked.

POST /api/auth/revoke

Request body:

{}

To revoke a specific access token owned by the current user, pass the plain token value:

Request body:

{
  "token": "2|plain-access-token"
}

Response status: 200 OK

{
  "message": "Token revoked successfully"
}

Endpoints

Method Path Authentication Success status Purpose
POST/api/auth/tokenNo200 OKCreate access and refresh tokens.
POST/api/auth/refreshNo200 OKRotate refresh token and get a new access token.
POST/api/auth/revokeBearer200 OKRevoke the current or specified access token.
GET/api/healthBearer200 OKCheck API service status.
GET/api/productBearer200 OKList products with filters and pagination.
POST/api/product/batchBearer200 OKFetch products by identifiers.
GET/api/product/{id}Bearer200 OKFetch one product by ultra_code or ultra_uuid.
GET/api/categoryBearer200 OKList categories.
GET/api/category/extra-pricesBearer200 OKList category extra prices for the authenticated user's store.
GET/api/category/{id}Bearer200 OKFetch one category by ultra_code or ultra_uuid.
GET/api/quantityBearer200 OKList product quantities.
POST/api/quantity/batchBearer200 OKFetch quantities by identifiers.
GET/api/quantity/{id}Bearer200 OKFetch one product quantity.
GET/api/brandBearer200 OKList brands.
GET/api/brand/{id}Bearer200 OKFetch one brand by ultra_code or ultra_uuid.
GET/api/changesBearer200 OKRead incremental catalog changes.
POST/api/orderBearer200 OKCreate an order.
GET/api/orderBearer200 OKList current user's orders.
GET/api/order/{id}Bearer200 OKFetch one current-user order with items.
GET/api/orders/allBearer200 OKList store-associated orders when permitted.

Products

List products

Query parameterTypeDefaultDescription
categorystringnullCategory ultra_uuid or ultra_code.
brandstringnullBrand ultra_uuid or ultra_code.
min_pricenumbernullMinimum price.
max_pricenumbernullMaximum price. Must be greater than or equal to min_price.
min_quantityintegernullMinimum quantity.
in_stockbooleannullWhen true, returns products with quantity greater than 0.
updated_sincedatenullReturns products updated after this date.
sortenumupdated_at_descprice_asc, price_desc, name_asc, name_desc, updated_at_desc, or updated_at.
limitinteger100Page size from 1 to 1000.
offsetinteger0Pagination offset.
load_without_imagebooleantrueWhen false, products without images may be excluded.
GET /api/product?category=CAT-001&in_stock=true&limit=50&offset=0

Response status: 200 OK

[
  {
    "ultra_code": "PRD-001",
    "ultra_uuid": "product-uuid",
    "product_name": {"ro": "Produs", "ru": "Product"},
    "description": "Description",
    "description_by_language": {
      "romanian": "Descriere",
      "russian": "Opisanie",
      "english": "Description"
    },
    "quantity": 12,
    "image_urls": ["https://cdn.example.com/product.jpg"],
    "brand": {
      "uuid": "brand-uuid",
      "name": "Brand",
      "updated_at": "2026-06-15T10:30:00"
    },
    "category": {
      "uuid": "category-uuid",
      "name": {"ro": "Categorie", "ru": "Category"},
      "updated_at": "2026-06-15T10:30:00",
      "level": null,
      "hierarchy": []
    },
    "user_price": null,
    "fixed_price": null,
    "price_d": null,
    "promo_b2b": null,
    "property_groups": [],
    "updated_at": "2026-06-15T10:30:00",
    "created_at": "2026-06-01T10:30:00"
  }
]
Response fieldWhere it maps on the site
ultra_codePublic product code used by integrations and usually shown as the product code/reference.
ultra_uuidInternal stable product UUID. Use it when your system stores UUID identifiers.
product_nameLocalized product title shown in product cards, category listings, search results, and product pages.
descriptionMain product description used on the product details page.
description_by_languageLocalized description values for Romanian, Russian, and English when available.
quantityAvailable stock used for availability labels and quantity checks before ordering.
image_urlsProduct gallery images used in cards and on product pages.
brand.uuid, brand.nameBrand attached to the product; used for brand filters and brand pages.
category.uuid, category.nameCategory attached to the product; used for category pages, filters, and breadcrumbs.
category.hierarchyCategory tree used to build navigation or breadcrumbs when hierarchy data exists.
user_priceDealer-specific price visible to the authenticated user when configured.
fixed_priceFixed product price override when configured.
price_dDistributor/dealer price data when available.
promo_b2bB2B promotional price or promo data when active.
property_groupsGrouped product characteristics/specifications shown on product pages.
updated_at, created_atTimestamps for synchronization, cache invalidation, and freshness checks.

Fetch one product

GET /api/product/PRD-001

Response status: 200 OK

The response is a single product object with the same fields as the list endpoint. If the product is not found, the API returns 404 Not Found.

Fetch products in batch

ultra_codes accepts up to 1000 values. Each value can be an ultra_code or ultra_uuid.

POST /api/product/batch

Request body:

{
  "ultra_codes": ["PRD-001", "PRD-002"]
}

Response status: 200 OK

The response is an array of product objects with the same fields as GET /api/product.

Categories

List categories

Use code to filter by category ultra_code.

GET /api/category?code=CAT-001

Response status: 200 OK

[
  {
    "uuid": "category-uuid",
    "code": "CAT-001",
    "name": {"ro": "Categorie", "ru": "Category"},
    "product_count": 120,
    "updated_at": "2026-06-15T10:30:00"
  }
]
Response fieldWhere it maps on the site
uuidStable category UUID used by integrations and product filters.
codeCategory code used in API filters and external catalog synchronization.
nameLocalized category title shown in navigation, category pages, and filters.
product_countNumber of products in the category; useful for category counters and filters.
updated_atCategory freshness timestamp for synchronization.

Fetch one category

Pass include_products=true to include a compact products list.

GET /api/category/CAT-001?include_products=true

Response status: 200 OK

{
  "uuid": "category-uuid",
  "code": "CAT-001",
  "name": {"ro": "Categorie", "ru": "Category"},
  "product_count": 2,
  "updated_at": "2026-06-15T10:30:00",
  "products": [
    {
      "ultra_code": "PRD-001",
      "product_name": {"ro": "Produs", "ru": "Product"},
      "quantity": 12,
      "price": {
        "amount": 100,
        "currency": "MDL"
      }
    }
  ]
}

products contains compact product rows for category previews or quick category synchronization. Product detail pages should still use GET /api/product/{id}.

Category extra prices

The authenticated user must be associated with a store.

GET /api/category/extra-prices

Response status: 200 OK

[
  {
    "id": 1,
    "category": {
      "uuid": "category-uuid",
      "code": "CAT-001",
      "name": {"ro": "Categorie", "ru": "Category"}
    },
    "price_extra": 5
  }
]
Response fieldWhere it maps on the site
categoryCategory to which the store-specific extra price rule applies.
price_extraExtra price percentage/value configured for the authenticated user's store and category.

Quantities

Quantity identifiers can be ultra_code or ultra_uuid.

EndpointParameters or payloadResponse
GET /api/quantitycategory, low_stockArray of quantity objects.
GET /api/quantity/{id}Path identifier.One quantity object.
POST /api/quantity/batch{"ultra_codes": ["PRD-001"]}Array of quantity objects.

List quantities

GET /api/quantity?category=CAT-001&low_stock=5

Response status: 200 OK

Fetch one quantity

GET /api/quantity/PRD-001

Response status: 200 OK

Fetch quantities in batch

POST /api/quantity/batch

Request body:

{
  "ultra_codes": ["PRD-001", "PRD-002"]
}

Response status: 200 OK

{
  "ultra_code": "PRD-001",
  "product_name": {"ro": "Produs", "ru": "Product"},
  "quantity": 12,
  "updated_at": "2026-06-15T10:30:00"
}
Response fieldWhere it maps on the site
ultra_codeProduct code used to match stock data with products in your system.
product_nameLocalized product title for stock reports or availability views.
quantityAvailable stock used for "in stock" labels, quantity selectors, and order validation.
updated_atLast product stock update timestamp.

Brands

Use GET /api/brand to list brands and GET /api/brand/{id} to fetch one brand.

GET /api/brand?code=BR-001

Response status: 200 OK

[
  {
    "code": "BR-001",
    "uuid": "brand-uuid",
    "name": "Brand",
    "product_count": 25,
    "updated_at": "2026-06-15T10:30:00"
  }
]

Fetch one brand

GET /api/brand/BR-001

Response status: 200 OK

The response is a single brand object with the same fields as the list endpoint.

Response fieldWhere it maps on the site
codeBrand code used in API filters and external synchronization.
uuidStable brand UUID used to connect products to a brand.
nameBrand name shown in product cards, product pages, brand filters, and brand pages.
product_countNumber of products attached to the brand.
updated_atBrand freshness timestamp for synchronization.

Changes

Use GET /api/changes for incremental synchronization. Store the returned next_since and use it as the next request's since value while has_more is true.

ParameterTypeRequiredDescription
sincedateYesReturn changes after this date.
entityenumNoproduct, price, quantity, category, or brand.
limitintegerNo1 to 1000. Default is 100.
GET /api/changes?since=2026-06-15T00:00:00&entity=product&limit=100

Response status: 200 OK

{
  "changes": [
    {
      "entity": "product",
      "action": "updated",
      "entity_id": "PRD-001",
      "changed_at": "2026-06-15T10:30:00.000000Z",
      "fields_changed": ["product"]
    }
  ],
  "next_since": "2026-06-15T10:30:00.000000Z",
  "has_more": false
}
Response fieldWhere it maps on the site
changesList of catalog entities that changed and should be refreshed in your local system.
entityChanged entity type: product, price, quantity, category, or brand.
actionChange action, for example updated.
entity_idIdentifier to use when reloading the changed product, category, brand, price, or stock data.
changed_atTimestamp of the change.
fields_changedHigh-level fields or data groups affected by the change.
next_sinceUse this value as the next request's since parameter.
has_moreWhen true, request the next page with next_since.

Orders

Create order

FieldTypeRequiredDescription
deliverystring or integerYesDelivery method accepted by the store configuration.
paymentstring or integerYesPayment method accepted by the store configuration.
shipping_dataobjectNoOptional customer, store, branch, address, city, and recipient data.
productsarrayYes1 to 1000 order lines.
products.*.ultra_uuidstringConditionalRequired when ultra_code is not provided.
products.*.ultra_codestringConditionalRequired when ultra_uuid is not provided.
products.*.quantityintegerYesQuantity, minimum 1.
POST /api/order

Request body:

{
  "delivery": "pickup",
  "payment": "cash",
  "shipping_data": {
    "storeId": "1",
    "filialId": "2",
    "client_type": "1",
    "name": "Customer Name",
    "phone": "+37300000000",
    "address": "Street 1",
    "city": "Chisinau",
    "recipient": "Customer Name"
  },
  "products": [
    {"ultra_code": "PRD-001", "quantity": 2},
    {"ultra_uuid": "product-uuid", "quantity": 1}
  ]
}

Response status: 200 OK

{
  "message": "Order created successfully",
  "order_id": 12345
}
Response fieldWhere it maps on the site
messageHuman-readable order creation result.
order_idInternal order ID used to open the order with GET /api/order/{id} and for support communication.

Read orders

  • GET /api/order returns orders that belong to the authenticated user.
  • GET /api/order/{id} returns one authenticated-user order and includes its items.
  • GET /api/orders/all returns store-associated orders when the user has access.
GET /api/order
GET /api/order/12345
GET /api/orders/all

Response status: 200 OK

{
  "order_id": 12345,
  "user_id": 10,
  "delivery": "pickup",
  "payment": "cash",
  "total": 1200.5,
  "status": 1,
  "created_at": "2026-06-15T10:30:00",
  "updated_at": "2026-06-15T10:45:00",
  "shipping_data": {
    "phone": "+37300000000",
    "name": "Customer Name",
    "storeId": "1",
    "filialId": "2",
    "client_type": "1",
    "delivery_status": null,
    "destination": null
  },
  "exchange_rate": null,
  "items": [
    {
      "product_ultra_uuid": "product-uuid",
      "product_name": {"ro": "Produs", "ru": "Product"},
      "quantity": 2,
      "price": 600.25
    }
  ]
}
Response fieldWhere it maps on the site
order_idOrder ID shown in order history and used for order details.
user_id, user_nameCustomer/dealer owner of the order. Some fields are returned only for detailed or associated-order views.
deliverySelected delivery method displayed in checkout and order details.
paymentSelected payment method displayed in checkout and order details.
totalOrder total shown in order history and order details.
statusNumeric order status used to render the current order state.
shipping_dataDelivery/contact data captured during checkout.
ultra_uuid, reserve_ultra_uuid, order_codeExternal/order-system identifiers returned for store-associated order views when available.
exchange_rateExchange rate used for the order when applicable.
itemsOrder line items returned by GET /api/order/{id}.
items.*.product_ultra_uuidProduct UUID for each ordered item.
items.*.product_nameProduct title shown in order item rows.
items.*.quantityOrdered quantity for the item.
items.*.priceUnit price stored on the order item.

Health

GET /api/health

Response status: 200 OK

{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2026-06-15T10:30:00",
  "services": {
    "database": "healthy",
    "cache": "healthy",
    "auth": "healthy"
  },
  "uptime_seconds": 1
}
Response fieldDescription
statusOverall API health: healthy or degraded.
versionApplication version configured on the server.
timestampServer-side health check timestamp.
services.databaseDatabase connectivity status.
services.cacheCache service read/write status.
services.authAuthentication provider configuration status.
uptime_secondsRequest/runtime uptime value reported by the application.

Errors and Status Codes

Error format

{
  "message": "Bad Request",
  "errors": {
    "field": ["Validation message"]
  }
}
{
  "message": "Invalid or expired token."
}

Status codes

  • 400: validation or business rule error.
  • 401: missing, invalid, expired, or revoked token.
  • 403: authenticated user is not allowed to access the resource.
  • 404: product, category, brand, order, or token was not found.
  • 429: rate limit exceeded.
  • 500: internal server error.

Integration notes

  • Keep access tokens in server-side storage and rotate them through /auth/refresh before expiry.
  • Use limit and offset for large catalog reads, and keep page sizes below 1000.
  • Prefer /changes for regular synchronization instead of repeatedly downloading the full catalog.
  • Retry 429 responses with backoff. Do not retry validation errors without changing the request.
  • Use stable identifiers: ultra_code for business integrations and ultra_uuid when the UUID is already stored in your system.