IVO
Back to help center
Merchant Integration

Merchant API reference

Full reference for the merchant API: authentication, every endpoint, and what each input and output field means.

Last updated: 26 August 2026

Get an API key

Sign in with a merchant account, open Settings → Integrations, select the API Keys tab, and create a key. Copy it immediately: the complete key is shown only once. You can revoke keys from the same page.

Base URL and authentication

https://a.ivo.md/v1/merchant-api

Send the key with every request using either header:

X-API-Key: <YOUR_API_KEY>
Authorization: Bearer <YOUR_API_KEY>

Always send Accept: application/json, and Content-Type: application/json except for file uploads. Without the Accept header, authentication failures are returned as an HTML error page instead of JSON.

Response envelope

CaseHTTPBody
Success200The endpoint's fields plus "status": "success".
Handled error202, 403, 404, 409, 422, 500"status": "error", a machine-readable message (the error code), and any extra context fields.
Authentication failure403Raised before the controller runs, so it has no status field — only {"message": "api_key_invalid"}. Codes: api_key_missing, api_key_invalid, merchant_not_found, integration_disabled.
Rate limited429120 requests per minute per IP address. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining.

Important: on a 200 response the top-level status field is always the literal string success — it says the call was accepted, nothing more. Endpoints that also have work in flight report it under a key of their own: sync_status on the sync endpoints, and import_status on the import endpoints.

A 202 response is an error envelope whose message says why the answer is not ready yet: queued, processing, or pending_approval.

Check the connection

GET /check

Validates the API key and returns the merchant identity. This is the endpoint to call first when wiring up an integration.

Input: none beyond the API key header.

{
  "merchant_id": "<MERCHANT_ID>",
  "merchant_name": "My Shop",
  "status": "success"
}
Output fieldTypeMeaning
merchant_idstringThe IVO identifier of the merchant this key belongs to. Store it: it never changes and it identifies the account in support requests.
merchant_namestringThe merchant's display name in IVO.
statusstringAlways success. It confirms the key is valid and not revoked; it is not the merchant's account status.

Merchant points

Every offer belongs to a merchant point (a shop, warehouse or pickup location). Use the point ID returned by these endpoints as merchant_point_id. If the account has exactly one point, product sync selects it automatically and you can omit the field.

GET /merchant-points

Returns all points belonging to the authenticated merchant.

{
  "points": [
    {
      "_id": "<POINT_ID>",
      "name": "Main store",
      "contact_person": "Ion Popescu",
      "email": "[email protected]",
      "phone": "+37360000000",
      "street_id": "<STREET_ID>",
      "number": "12",
      "city_id": "<CITY_ID>",
      "state_id": "<STATE_ID>",
      "country": "MD",
      "zip_code": "MD-2001",
      "status": "approved",
      "is_active": true
    }
  ],
  "status": "success"
}
Output fieldTypeMeaning
points[]._idstringThe point ID. This is the value to send as merchant_point_id.
points[].namestringThe point name. It can also be sent as merchant_point instead of the ID; the match is exact and case-insensitive.
points[].contact_person, email, phonestringWho the courier and IVO support contact at this location.
points[].street_id, number, block, entrance, floor, apartment, intercom, zip_code, city_id, state_id, countrystringThe pickup address. The location IDs are IVO's own street/city/state identifiers.
points[].statusstringpending (waiting for IVO approval), approved, or rejected. Offers at a point that is not approved stay invisible on the site.
points[].is_activebooleanThe merchant's own on/off switch. Offers at an inactive point stay invisible.
points[].rejection_reasonstring, nullWhy IVO rejected the point, when status is rejected.

POST /merchant-points/store

Creates a point. It is always created with status: pending and is_active: true, and stays invisible to buyers until IVO approves it.

Input fieldTypeMeaning
namestringHow the location is named in your system and in the merchant panel.
contact_personstringThe person the courier asks for at pickup.
email, phonestringContact details for this location.
street_id, city_id, state_idstringIVO location identifiers for the address.
number, block, entrance, floor, apartment, intercomstringThe rest of the street address, so the courier reaches the right door.
zip_code, countrystringPostal code and country code.

Any field outside this list is ignored. The response is {"point": { … }, "status": "success"}, where point is the created record including its new _id.

POST /merchant-points/update/{id}

Updates a point owned by the merchant. {id} is the point ID. Accepts every creation field plus is_active (boolean — set it to false to take the location's whole stock offline without deleting anything). Returns the updated point. Unknown ID: point_not_found (404).

POST /merchant-points/delete/{id}

Removes a point owned by the merchant. Returns {"deleted": true, "status": "success"}. The record is soft-deleted, so its history is kept, but the point can no longer be used for offers.

Update an existing offer

POST /product-offer/update

Updates price or stock on an offer that already exists, without running the product import. This is the cheapest and fastest endpoint — use it for regular price and stock refreshes.

{
  "merchant_internal_id": "PRODUCT-123",
  "merchant_point_id": "<POINT_ID>",
  "price": 1999,
  "currency": "MDL",
  "availability": 5,
  "availability_on_order": 0
}
Input fieldTypeMeaning
offer_idstringThe IVO offer ID. Send this or merchant_internal_id. The offer must belong to your merchant account.
merchant_internal_idstringYour own product identifier (SKU), as sent when the offer was created. Searched within your account only.
merchant_point_idstringOnly used together with merchant_internal_id: it narrows the search to one point. Required when the same SKU exists at several points, otherwise the call fails with merchant_internal_id_not_unique.
pricenumberThe new selling price, in currency. An offer with a price of 0 or empty is never shown on the site.
currencystringISO 4217 code. Accepted: MDL (default), EUR, USD, RON, UAH, RUB, GBP. Prices in another currency are converted for display at IVO's stored rate.
availabilityintegerUnits physically in stock at this point and shippable immediately.
availability_on_orderintegerUnits you can supply on order (supplier stock). Keeps the product buyable when availability is 0, with a longer delivery time.

At least one of availability, availability_on_order, price or currency must be present; sending only an identifier fails with no_fields_to_update. Fields you omit keep their current value. Every change is written to the offer's price/stock history.

{
  "offer": {
    "id": "<OFFER_ID>",
    "merchant_internal_id": "PRODUCT-123",
    "product_id": "<PRODUCT_ID>",
    "merchant_point_id": "<POINT_ID>",
    "condition": "new",
    "quality": null,
    "price": 1999,
    "currency": "MDL",
    "availability": 5,
    "availability_on_order": 0
  },
  "status": "success"
}
Output fieldTypeMeaning
offer.idstringThe offer ID. Reuse it as offer_id to skip the SKU lookup on later calls.
offer.merchant_internal_idstring, nullYour SKU as stored on the offer.
offer.product_idstringThe IVO product (the variant) this offer is attached to.
offer.merchant_point_idstringThe point the offer is stocked at.
offer.conditionstringnew, used or refurbished.
offer.qualitystring, nullFree-text grade used to tell apart several non-new offers of the same product.
offer.price, offer.currency, offer.availability, offer.availability_on_ordernumber / string / integerThe stored values after the update.

Errors: missing_identifier (422) — neither offer_id nor merchant_internal_id was sent; no_fields_to_update (422); offer_not_found (404) — no such offer, or it belongs to another merchant; merchant_internal_id_not_unique (409) — the SKU matches several offers, and the body carries count. Send merchant_point_id to disambiguate.

Product and offer information

GET /product/{id}/info

Use the offer ID as {id}, not a product ID. The offer must belong to the authenticated merchant.

{
  "product": {
    "id": "<OFFER_PRODUCT_ID>",
    "root_product_id": "<ROOT_PRODUCT_ID>",
    "type": "variant",
    "status": "active",
    "name": {"ro": "Nume", "en": "Name", "ru": "Название"},
    "slug": {"ro": "nume", "en": "name", "ru": "nazvanie"},
    "url": "https://ivo.md/nume/p",
    "urls": {"ro": "https://ivo.md/nume/p", "en": "https://ivo.md/en/name/p", "ru": "https://ivo.md/ru/nazvanie/p"}
  },
  "offer": {
    "id": "<OFFER_ID>",
    "price": 1999,
    "currency": "MDL",
    "availability": 5,
    "availability_on_order": 0
  },
  "history": [
    {
      "price": 2099,
      "currency": "MDL",
      "availability": 3,
      "availability_on_order": 0,
      "changed_at": "2026-08-20T11:04:35+03:00",
      "source": "merchant_api"
    }
  ],
  "status": "success"
}
Output fieldTypeMeaning
product.idstringThe product the offer is attached to. For a product with variants this is the variant, not the parent.
product.root_product_idstringThe main (parent) product. Several variants share it, and the public page belongs to it.
product.typestringmain or variant.
product.statusstringThe catalogue status of the offer's product, e.g. active or draft.
product.nameobjectThe catalogue name per language (ro, en, ru). IVO writes it, so it can differ from the name you sent.
product.slugobjectThe URL segment per language.
product.urlstringThe Romanian public URL — the shortest link to give a buyer.
product.urlsobjectThe public URL per language.
offer.id, offer.price, offer.currency, offer.availability, offer.availability_on_orderThe offer's current values, as IVO stores them right now.
history[]arrayUp to 100 price/stock changes, most recent first. Each entry carries price, currency, availability, availability_on_order, changed_at (ISO 8601) and source — which system made the change (merchant_api, api_sync, and so on).

Errors: offer_not_found (404); product_not_accessible (403) — the offer belongs to another merchant; product_not_found (404); pending_approval (202) — the main product is not published yet, and the body carries product_id and product_status; product_slug_missing (422).

POST /product/info-by-sku

The same detailed answer, found by your own SKU instead of an IVO ID. Use it when you do not store IVO identifiers on your side.

{
  "import_name": "daily-sync",
  "merchant_internal_id": "PRODUCT-123"
}
Input fieldTypeMeaning
merchant_internal_idstring, requiredYour SKU. The lookup searches the imported rows of your merchant account.
import_namestring, requiredValidated as required, but the lookup does not filter by it: a SKU is searched across all of your imports. Send the same name you used on /product/sync.

On success the body is identical to GET /product/{id}/info. While the product is still travelling through the pipeline, the answer is an error envelope:

messageHTTPMeaning
not_imported404No imported row carries this SKU. Send the product with /product/sync first.
queued202The row is waiting to be processed. Poll again.
processing202The row is being processed right now. Poll again.
pending_approval202The product exists but is not published yet. Nothing to do on your side.
the row's error code500Processing failed. The body carries import_row_id, import_id, ai_product_id, ai_error and error.
the row's offer status404The row completed without producing an offer (for example unauthorized_category).
offer_deleted404The row points at an offer that no longer exists.

Create or synchronize one product

POST /product/sync

Creates a product or updates an existing offer. Reusing the same import_name with a stable merchant_internal_id updates the same row instead of creating duplicates, so this endpoint is safe to call repeatedly for the same catalogue.

{
  "mode": "async",
  "import_name": "daily-sync",
  "merchant_internal_id": "PRODUCT-123",
  "name": "Example Phone 256 GB Black",
  "price": 1999,
  "currency": "MDL",
  "availability": 5,
  "availability_on_order": 0,
  "merchant_point_id": "<POINT_ID>",
  "brand": "Example",
  "ean": "5940000000000",
  "description": "Product description",
  "images": ["https://example.com/product.jpg"]
}
Input fieldTypeMeaning
pricenumber, requiredSelling price, greater than 0 (minimum 0.01). Must be a plain number — 1999 or 1999.00, not "1 999,00".
namestring, required without product_idThe full product name. IVO matches it against the catalogue and creates the product from it, so include the brand, model and the distinguishing attributes (capacity, colour). A name that is too vague is rejected with the row error too_generic.
product_idstringAn existing IVO product ID. Bypasses catalogue matching completely and only writes the offer — the fastest path when you already know the product. If that product already has variants, you must also send offer_product_id.
offer_product_idstringThe variant of product_id the offer attaches to. Required when the main product has variants, because IVO never guesses which variant you are stocking.
merchant_internal_idstringYour own stable product identifier (SKU). It is the key that links your catalogue to IVO on every later call. Strongly recommended: without it, rows are matched by a hash of the whole payload.
legacy_merchant_internal_id / legacy_merchant_internal_idsstring / arrayIdentifiers this product used to be sent under. Use them when your SKU scheme changes, so the existing row and offer are reused instead of a duplicate product being created.
import_namestringThe name of the import list this product belongs to, and the key you poll status with. It is slugified: spaces and punctuation become _ or are dropped, and it is cut to 64 characters ("My Daily Sync"my_daily_sync). Empty means default. Use one stable name per source.
merchant_point_id / merchant_pointstringThe point ID, or the exact point name. If the account has a single point, it is selected automatically.
availabilityinteger or stringImmediate stock. Text is parsed leniently: "10 pcs" → 10, "2-3" → 3, "in stock"/"da" → 1, "out of stock" and anything unrecognised → 0. If neither availability nor availability_on_order is sent, immediate stock defaults to 1.
availability_on_orderinteger or stringStock available on order, parsed the same way.
currencystringISO 4217 code, MDL by default.
conditionstringnew (default), used or refurbished. One new offer exists per product and point; non-new offers may be several.
qualitystringFree-text grade that tells apart several non-new offers of the same product at the same point.
brandstringBrand name. Helps matching and is used when the product has to be created.
ean, asin, janstringGlobal product barcodes/identifiers. The strongest matching signal there is — send them when you have them.
descriptionstringProduct description used for matching and, for a new product, as source material.
images (or image)array or stringImage URLs, first one first. IVO downloads them; they must be publicly reachable.
weight, volumenumberShipping weight (grams) and volume, used for delivery pricing.
item_group_id, item_group_title, variant_option, variant_optionsstring / objectVariant grouping hints: products sharing an item_group_id are treated as variants of one parent, and the option values say what distinguishes them (colour, size).
force_reprocessbooleantrue restarts the row from zero, dropping previous matching results. Use it to recover a row that was matched wrongly — not on every sync, because it re-runs the whole matching pipeline.
modestringasync (default) returns immediately. sync holds the connection until the row finishes, up to a server-side limit (300 seconds by default), and returns anyway if the work is still running.

Any other field is stored with the row but is only understood if its name matches an import column.

Responses

All three are HTTP 200 with "status": "success". Which fields are present tells you what happened.

Fields returnedWhat happened
sync_status: "completed", offer_id, offer_product_id, message: "Offer saved (AI bypassed)"You sent product_id: the offer was written directly, with no matching and no approval step.
sync_status: "completed", offer_id, offer_product_id, import_id, row_id, and matched_by when a legacy identifier was usedAn existing offer with this merchant_internal_id was found and updated immediately. This is the normal answer for a product already in the catalogue.
sync_status: "processing", import_id, message: "Product creation started"A new — or materially renamed — product entered the import and approval flow. Poll /product-import/status or /product/info-by-sku for the outcome.
Output fieldTypeMeaning
sync_statusstringcompleted — the offer exists and nothing is pending. processing — the product was queued and you must poll for the outcome. This is the field to branch on, not status.
offer_idstringThe created or updated offer. Store it: it makes later price/stock updates a single fast call.
offer_product_idstringThe IVO product (variant) the offer hangs on.
import_idstringThe import list this product was filed under — the one named by import_name.
row_idstringThe import row for this product.
matched_bystringmerchant_internal_id, or legacy_merchant_internal_id when the offer was found through one of the old identifiers you supplied.
row_statusstringOnly in mode: sync when the wait expired: the row's state at that moment.

Errors: invalid_product_id (422), product_not_found (404), offer_product_not_found (404), offer_product_invalid (422) — the variant does not belong to that main product, missing_offer_product_id (422), missing_merchant_point (422) — no point could be resolved on the product_id path, invalid_price (422), unauthorized_category (403) — your account is not authorized to sell in that category, integration_not_found / integration_disabled (403) — the import_name names an integration that was deleted or switched off, and processing_failed (422) in mode: sync with error and import_id.

Synchronize multiple products

POST /product/sync-multiple

Between 1 and 100 products per request. Each entry of products takes exactly the fields of /product/sync, plus the extras below. This is the endpoint to build a full catalogue sync on.

{
  "mode": "async",
  "import_name": "daily-sync",
  "image_base_url": "https://shop.example.com/images",
  "products": [
    {
      "merchant_internal_id": "PRODUCT-123",
      "name": "Example Phone 256 GB Black",
      "price": 1999,
      "availability_by_point": {
        "<POINT_ID_1>": 5,
        "<POINT_ID_2>": 2
      }
    }
  ]
}
Batch fieldTypeMeaning
productsarray, required1 to 100 product objects. Split a larger catalogue into consecutive requests with the same import_name.
import_namestringSame rules as on /product/sync, and the same list: the two endpoints share an import when the name matches.
modestringasync (default) or sync.
image_base_urlstring (URL)Prefix prepended to relative image paths, so a feed can send /catalog/x.jpg. Stored on the import and reused on retries.
image_split_spacesbooleanTreat spaces inside an image value as separators between several paths.
image_split_commasbooleanTreat commas inside an image value as separators between several paths.
integration_run_idstring, max 64Marks every row of one full catalogue run. It lets IVO measure progress against the run's real total instead of the size of one batch, and lets a new run discard stock left by the previous configuration.
Per-product extraTypeMeaning
availability_by_pointobjectStock per point: {"<POINT_ID>": 5}. Send the complete picture for that SKU in one entry — it replaces the previous snapshot. The product then lands on a single import row instead of one row per point.
availability_onlybooleantrue makes the entry a pure stock update: only merchant_internal_id and availability are required, name and price are not. Unknown identifiers are skipped instead of creating products — this is a refresh stream, not a product source.
force_reprocessbooleanSkips the fast existing-offer path and restarts the row's matching.

Response: sync_status (completed when every product was already up to date, processing when rows were queued) and import_id, plus message when work was queued. Nothing is returned per product — read individual outcomes from /product-import/status or /product/info-by-sku. A payload that fails validation is rejected as a whole with validation_failed (422) plus the index of the offending product and its errors; also invalid_product_payload (422) and products_required (422).

Upload a product file

POST /product-import/upload-and-import

Sends a whole catalogue file in one request, exactly like uploading it in the merchant panel. Use multipart/form-data.

Input fieldTypeMeaning
filefile, requiredThe catalogue file. Accepted extensions: XLSX, XLS, CSV, TSV, ODS, JSON and XML.
modestringasync (default) returns as soon as the file is queued. sync waits for the import to finish, up to 300 seconds by default.
import_namestringOptional name to file this upload under, slugified like everywhere else. Pass one when you intend to poll the import: GET /product-import/status then finds it by that name. Without it the import keeps the uploaded file's own name, and you poll by import_id (or by that exact file name).
columnsarrayColumn mapping, one entry per column of the file in order. Use the canonical names: name, description, brand, ean, asin, jan, weight, volume, image, availability, availability_on_order, price, price_and_currency, currency, merchant_internal_id, and ignore for columns to skip. Map several columns to image to import several images. When columns is omitted, IVO detects the mapping from the header row.
Output fieldTypeMeaning
import_idstringThe created import. Keep it: it is what you poll the status endpoint with.
import_namestringThe name the import was filed under — your import_name after slugification, or the uploaded file's name. Poll with exactly this value.
import_statusstringThe import's real state: pending, ready_to_process, processing, completed, completed_with_errors or error.
messagestringImport started successfully, or a note that the work is still running in mode: sync.
importobjectOnly in mode: sync when the import reached a final state: the full import record.

For recurring synchronization prefer /product/sync-multiple: it updates the same rows in place, reports per-product outcomes, and needs no file.

Check import status

GET /product-import/status

Reports the progress of any import you created — through /product/sync, /product/sync-multiple or /product-import/upload-and-import — and the outcome for one product in it.

GET /product-import/status?import_name=daily-sync&merchant_internal_id=PRODUCT-123
GET /product-import/status?import_id=<IMPORT_ID>
Input fieldTypeMeaning
import_idstringThe import to report on, as returned by any sync or upload call. Send this or import_name; sending neither fails with missing_identifier (422).
import_namestringThe import list name. Matched both slugified (as sync files it) and exactly as written (as an upload files it), so the name you were handed back always resolves. Must belong to your merchant account.
merchant_internal_idstringWhich product's row to report. Can be omitted only when the import holds exactly one row; on a multi-row import, omitting it fails with merchant_internal_id_required (422).
{
  "import_id": "<IMPORT_ID>",
  "import_name": "daily-sync",
  "import_status": "processing",
  "error_type": null,
  "processed_rows": 87,
  "total_rows": 120,
  "row_count": 120,
  "row": {
    "id": "<ROW_ID>",
    "row_number": 12,
    "status": "completed",
    "error": null,
    "merchant_internal_id": "PRODUCT-123",
    "ai_product_id": "<AI_PRODUCT_ID>",
    "offer_product_id": "<VARIANT_ID>",
    "product_id": "<PRODUCT_ID>",
    "offer_id": "<OFFER_ID>",
    "product": {
      "id": "<VARIANT_ID>",
      "root_product_id": "<ROOT_PRODUCT_ID>",
      "type": "variant",
      "root_type": "main",
      "status": "active",
      "name": {"ro": "Nume", "en": "Name", "ru": "Название"},
      "slug": {"ro": "nume", "en": "name", "ru": "nazvanie"},
      "url": "https://ivo.md/nume/p",
      "urls": {"ro": "https://ivo.md/nume/p"},
      "has_public_url": true
    },
    "started_at": "2026-08-26T09:12:00+03:00",
    "ended_at": "2026-08-26T09:12:41+03:00"
  },
  "status": "success"
}
Output fieldTypeMeaning
import_idstringThe import this request resolves to.
import_namestringThe name it is filed under.
import_statusstringThe state of the whole import: pending, ready_to_process, processing, completed, completed_with_errors or error. This is the import's state — the top-level status only says the request itself succeeded.
processed_rows / total_rowsintegerProgress of the whole import: rows that reached completed or error, out of the expected total. Equal values mean the import is finished.
row_countintegerHow many rows the import currently holds.
error_typestring, nullSet when the import itself failed (an unreadable file, for instance), rather than an individual row.
row.statusstringThe state of the selected product: ready (queued), processing, completed, or error. This is the field to poll for one SKU.
row.errorstring, nullWhy this row failed, as a machine-readable code — see the row-level codes below.
row.merchant_internal_idstring, nullYour SKU as stored on the row.
row.ai_product_idstring, nullThe intermediate record produced while the product was being built. Useful only for support requests.
row.product_id / row.offer_product_idstring, nullThe matched product, and the variant the offer attaches to.
row.offer_idstring, nullThe resulting offer. Once it is present, later updates can go through /product-offer/update.
row.productobject, nullPublic product information: name and slug per language, url and urls, catalogue status, and has_public_urlfalse while the product has no live page yet.
row.started_at / row.ended_atdatetime, nullWhen processing of this row began and ended.
statusstringAlways success — it reports that the request itself succeeded. The import's own state is import_status.

Errors: missing_identifier (422) — neither import_id nor import_name was sent, import_not_found (404), import_row_not_found (404), merchant_internal_id_required (422).

When an offer becomes visible

A successful call is not the same as a live product. An offer appears on ivo.md only when all of the following hold:

  • the merchant account is active;
  • its merchant point is approved and is_active;
  • the product has passed IVO's approval and has a public page;
  • price is present and greater than zero;
  • availability or availability_on_order is greater than zero.

If a synced product does not appear, check these five in order before opening a support request.

Common error codes

CodeHTTPMeaning
api_key_missing403No X-API-Key and no Authorization: Bearer header.
api_key_invalid403Unknown or revoked key.
merchant_not_found403The key is valid but its merchant account no longer exists.
integration_disabled / integration_not_found403The import_name names an integration that the merchant switched off or deleted.
unauthorized_category403Your account is not authorized to sell in that category.
product_not_accessible403The offer belongs to another merchant.
offer_not_found, product_not_found, offer_product_not_found, point_not_found, import_not_found, import_row_not_found, not_imported, offer_deleted404The named record does not exist, or does not belong to your account.
merchant_internal_id_not_unique409The SKU matches several offers. Add merchant_point_id.
missing_identifier, no_fields_to_update, invalid_price, invalid_product_id, missing_merchant_point, missing_offer_product_id, offer_product_invalid, merchant_internal_id_required, products_required, invalid_product_payload, validation_failed, product_slug_missing, processing_failed422The payload is incomplete or inconsistent. validation_failed carries index and errors.
queued, processing, pending_approval202Not an error: the answer is not ready yet. Poll again.

Row-level import errors appear in row.error rather than as an HTTP status: too_generic (the name does not identify a specific product), missing_name_value, missing_category, unauthorized_category, variant_not_found, duplicate, no_availability, and missing_merchant_point.

Was this article helpful?

Share quick feedback so we can improve it.

Related articles

Merchant Integration

Extensions: how they work

A generic overview of how IVO marketplace extensions typically install, authorize, sync, and report status.

Merchant Integration

Magento 2 extension: installation

Install the IVO Marketplace Magento 2 extension and connect your store to IVO.

Merchant Integration

OpenCart 4 extension: installation

Install the IVO Marketplace OpenCart extension and connect your store to IVO.

Merchant Integration

PrestaShop module: installation

Install the IVO Marketplace PrestaShop module and connect your store to IVO.