HikaShop Connector API

https://your-shop.tld/hikashop-api/v1 YAML JSON MARKDOWN

Overview

The connector plugin ships inside HikaShop Business and answers JSON over HTTPS. The mobile application is one client of it; anything it does, your own code can do.

Every response carries the same envelope, {"data": …, "meta": …, "error": null}, and every route except POST /pair carries a device token as Authorization: Bearer <token>. A token holds the scopes read and optionally write, and the operator it is bound to keeps their own access levels, so a device may hold write and still be refused an order its operator cannot see.

The base path is a plugin parameter and a shop can move it. Nothing else here changes between shops.

the envelope
{
  "data": { … },
  "meta": null,
  "error": null
}

Pairing a device

A client never sees the merchant's password. It is given a pairing code instead, generated in System > App Devices of the backend, and exchanges it once for a token of its own.

The code is nine characters, lives five minutes, may be tried five times before it dies, and can only be spent once. POST /pair is rate limited to thirty attempts per minute per address, which is the only route that answers without a token.

What comes back is the token, the scopes the code granted, and the id of the device row. The token is shown once and stored only as a SHA-256 hash, so a device that loses it pairs again rather than recovering it. Revoking a device is deleting its row in that same screen, and every token it held stops working at once.

the exchange
curl -X POST "$SHOP/hikashop-api/v1/pair" \
  -H "Content-Type: application/json" \
  -d '{ "code": "K7QM-2F84", "device_name": "Counter tablet" }'

# then, for everything else
curl "$SHOP/hikashop-api/v1/products" \
  -H "Authorization: Bearer $TOKEN"

Scopes and access levels

Two separate checks run on every request, and the narrower one wins.

The scope is what the device may do at all: read, and write if the pairing code granted it. A device without write is refused every route that changes anything, whoever holds it.

The access levels are the shop's own, belonging to the operator the device is bound to. They decide which records that person may see and change, exactly as they do in the backend. So a device holding write can still be refused an order, and a device holding only read sees a smaller catalogue than another one.

This is why an integration should be given its own device rather than borrowing one: revoke it and nothing else is disturbed, and its operator's access levels are the ceiling on what it can reach.

refused for the scope
{
  "data": null,
  "error": {
    "code": "forbidden",
    "message": "This device does not have the required scope."
  }
}

The envelope, errors and paging

Every JSON answer has the same three keys. data is the payload, meta carries paging and other context when there is any and is null otherwise, and error is null on success.

On failure data is null and error holds a stable code and a human message. Read the code, not the message: the message is written for a person and may be translated or reworded, the code is what your own logic should branch on.

Listings page with start and limit in the query, and answer with start, limit and total in the envelope's meta. total counts the rows matching the filter before paging, so it is how you know there is another page. limit is capped, usually at a hundred, and asking for more silently gets you the cap rather than an error.

One thing to note about a listing: data is the list itself, not an object wrapping it. The paging lives in meta.

Codes every route can return

CodeHTTPMeans
invalid_request400A required field is missing or malformed.
unauthorized401No token, or a token that is unknown, revoked or unpublished.
forbidden403The device lacks the scope, or the operator lacks the access level.
not_found404No such record, or no such route. Also returned when the operator may not see the record, so that it cannot be probed for.
too_many_requests429Rate limited. Only /pair does this.
a page of a listing
{
  "data": [ … ],
  "meta": { "start": 0, "limit": 20, "total": 307 },
  "error": null
}

Calling it from a browser

The API answers cross-origin requests, because the application is a web application as well as a phone one. Access-Control-Allow-Origin is *, the allowed methods are GET, POST, PUT, DELETE and OPTIONS, and the allowed headers are Authorization, Content-Type and X-Hikashop-Token. A preflight is answered immediately and may be cached for a day.

* with a bearer token is deliberate and safe in the way cookies would not be: nothing is sent automatically by the browser, so a page on another origin can only call this API if it already holds a token, and a token is only ever obtained by pairing.

Send the token in the Authorization header. Never put it in the query string, where it would be written to every access log between you and the shop.

the preflight
OPTIONS /hikashop-api/v1/products

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Hikashop-Token
Access-Control-Max-Age: 86400

Adding your own routes

A HikaShop plugin can serve paths of its own through the same base path, the same envelope and the same authentication, by listening to three events.

onConnectorBeforeRoute fires before any matching, so it can intercept a path the connector also serves. onConnectorRoute fires only when nothing matched, which is where a new path belongs. onConnectorBeforeResponse fires just before the JSON is written, for adding a field to an answer somebody else built.

The listener receives one object by reference, carrying path (the part after the base path), method, base_path, response, and handled. Authenticate with requireScope() exactly as the connector's own routes do, answer through $ctx->response, and set $ctx->handled = true so the router stops rather than falling through to its 404.

a plugin serving its own path
public function onConnectorRoute(&$ctx) {
    if ($ctx->path !== 'warehouse/stock' || $ctx->method !== 'GET')
        return;

    $device = HikashopConnectorAuth::requireScope($ctx->response, 'read');
    if ($device === null)
        return;          // it has already answered 401 or 403

    $ctx->response->data(array('pallets' => $this->countPallets()));
    $ctx->handled = true;
}

What this shop is

GET /site read since 6.6.0

The first call a client makes. It says what it has connected to, who it is connected as, and what that operator is allowed to do, which is enough to draw an interface without asking for anything it will only be refused.

Nothing here changes often. Cache it, and use GET /version to know when to look again.

Response

FieldTypeDescription
appstringAlways hikashop-connector. A cheap way to be sure you are talking to this API and not to something else answering on that path.
api_versionstringSemver of the API itself, not of HikaShop. A minor bump adds fields or routes, a major one breaks something.
site_namestringWhat the site calls itself, which is what a merchant recognises the shop by.
hikashop_versionstring|nullThe HikaShop release running there.
cmsobjectWhat it is running on.
namebooleanjoomla or wordpress.
versionstring|nullThe CMS release.
editionstringstarter, essential or business. This API only answers on Business, so in practice it is business unless the licence has lapsed.
logostringThe shop's logo as an absolute URL, empty when none is set. The same setting the invoices use.
currencyobjectThe shop's money.
defaultintegerThe currency id, a row in the shop's own table, not an ISO code.
price_with_taxbooleanWhether prices are shown to customers with tax included. Display only: the prices in this API are as stored.
operatorobjectThe user the device is bound to.
idinteger0 when the device is bound to nobody, which is a device paired without an operator.
namestring|nullTheir display name.
rolestringadmin when they may manage the component, staff otherwise.
scopesstring[]What this device may do: read, and write when it was granted.
permissionsobjectWhat the operator's access levels allow, keyed by resource (product, order, category, discount, user, zone, characteristic, massaction, dashboard), each with view, manage and delete. Use it to leave controls out rather than showing a screen full of refusals. Its keys are the resources this HikaShop knows about, so an add-on can add one.
capabilitiesobjectWhat this shop can do beyond the basics.
posbooleanReserved for the point of sale, false for now.
pushbooleanReserved for push notifications, false for now.
multivendorbooleanWhether HikaMarket is installed.
GET/site
curl "$SHOP/hikashop-api/v1/site" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "app": "hikashop-connector",
        "api_version": "1.0.0",
        "site_name": "HikaShop Test",
        "hikashop_version": "6.5.1",
        "cms": {
            "name": "joomla",
            "version": "6.1.1"
        },
        "edition": "essential",
        "logo": "",
        "currency": {
            "default": 1
        },
        "price_with_tax": true,
        "operator": {
            "id": 0,
            "name": null,
            "role": "staff"
        },
        "scopes": [
            "read",
            "write"
        ],
        "permissions": {
            "category": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "characteristic": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "product": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "order": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "discount": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "user": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "zone": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "dashboard": {
                "view": true,
                "manage": true,
                "delete": true
            },
            "massaction": {
                "view": true,
                "manage": true,
                "delete": true
            }
        },
        "capabilities": {
            "pos": false,
            "push": false,
            "multivendor": false
        }
    },
    "meta": null,
    "error": null
}

The settings that decide how prices read

GET /settings read since 6.6.0

The handful of configuration flags a client needs in order to show the same figures the shop's own pages show. They are read only here; they are changed in the backend.

Response

FieldTypeDescription
product_contactbooleanWhether products can be enquired about rather than bought.
product_waitlistbooleanWhether customers can join a waiting list for something out of stock.
price_with_taxbooleanShow prices with tax included.
floating_tax_pricesbooleanWhether the tax shown depends on the customer, which means a price cannot be cached across customers.
show_original_pricebooleanShow the price before a discount alongside the discounted one.
round_calculationsintegerWhen rounding happens during a calculation. Match it or your totals will differ from the shop by a cent.
GET/settings
curl "$SHOP/hikashop-api/v1/settings" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "product_contact": false,
        "product_waitlist": false,
        "price_with_tax": true,
        "floating_tax_prices": false,
        "show_original_price": false,
        "round_calculations": 0
    },
    "meta": null,
    "error": null
}

Change tokens for the cacheable resources

GET /version read since 6.6.0

Three opaque tokens, one for each resource worth caching. A token changes when its resource does, so a client keeps the copy it has until the token moves.

Compare them, do not read them. They are strings today and their meaning is deliberately not part of this contract.

This is what makes a dictionary of several thousand strings affordable: fetch it once, ask this cheap route afterwards.

Response

FieldTypeDescription
i18nstringMoves when the shop's translations change.
statusesstringMoves when an order status is added, renamed or unpublished.
languagesstringMoves when the shop's languages change.
GET/version
curl "$SHOP/hikashop-api/v1/version" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "i18n": "1786385909",
        "statuses": "1834471105",
        "languages": "1820052638"
    },
    "meta": null,
    "error": null
}

The shop's order statuses

GET /statuses read since 6.6.0

Every published order status, in the order the backend shows them. A shop invents its own, so this list is not a fixed set and should never be hard coded.

The namekey is the identifier to send to POST /orders/{id}/status; the name is for a person to read.

Response a list

FieldTypeDescription
namekeystringThe identifier. This is what you send when changing an order.
namestringTranslated into the operator's language, ready to display.
label_keystringThe translation key behind that name, if you would rather translate it yourself.
colorstringThe colour the backend uses for this status, empty when none is set.
GET/statuses
curl "$SHOP/hikashop-api/v1/statuses" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "namekey": "created",
            "name": "created",
            "label_key": "ORDER_STATUS_CREATED",
            "color": ""
        },
        {
            "namekey": "confirmed",
            "name": "confirmed",
            "label_key": "ORDER_STATUS_CONFIRMED",
            "color": ""
        },
        {
            "namekey": "cancelled",
            "name": "cancelled",
            "label_key": "ORDER_STATUS_CANCELLED",
            "color": ""
        },
        {
            "namekey": "refunded",
            "name": "refunded",
            "label_key": "ORDER_STATUS_REFUNDED",
            "color": ""
        },
        "… 4 more, trimmed for the example"
    ],
    "meta": null,
    "error": null
}

HikaShop's own translation of a locale

GET /i18n read since 6.6.0

The shop's translation dictionary for one locale, so that a client can name things exactly as the merchant's own site names them, including whatever they have overridden.

It is large. Cache it against the i18n token from GET /version rather than fetching it again.

Query

ParameterTypeDescription
localestringA HikaShop language tag such as en-GB. Falls back to the site language when it is not installed.

Response

FieldTypeDescription
localestringThe locale actually served, which may not be the one asked for.
stringsobjectTranslation key to translated text. Thousands of keys, and a shop can add or override any of them, so there is no list to give.
GET/i{id}n?locale=en-GB
curl "$SHOP/hikashop-api/v1/i{id}n?locale=en-GB" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "locale": "en-GB",
        "strings": {
            "PRICE_BEGINNING": "",
            "PRICE_BEFORE_ORIG": " (",
            "PRICE_AFTER_ORIG": ") ",
            "PRICE_DISCOUNT_START": "",
            "PRICE_DISCOUNT_END": "",
            "PRICE_BEFORE_TAX": " (",
            "PRICE_AFTER_TAX": " excl VAT) ",
            "PRICE_END": "",
            "FREE_PRICE": "Free",
            "PRICE_SEPARATOR": "<br/>",
            "PER_UNIT_AT_LEAST_X_BOUGHT": " per unit for buying at least %s",
            "PER_UNIT": " each",
            "ITEM_NOT_SOLD_ANYMORE": "Item not sold anymore",
            "ITEM_SOLD_ON_DATE": "This item will be sold starting on %s",
            "ADD_TO_CART": "Add to cart",
            "NO_STOCK": "No stock",
            "X_ITEMS_IN_STOCK": "%s items in stock",
            "X_ITEMS_IN_STOCK_ONE": "%s item in stock",
            "X_ITEMS_IN_STOCK_1": "%s item in stock",
            "SPECIFICATIONS": "Specifications",
            "REFRESH_INFORMATION": "Refresh information",
            "NO_VALUES_FOUND": "No values found",
            "PRODUCT": "Product",
            "CART_PRODUCT_NAME": "Name",
            "CART_PRODUCT_QUANTITY": "Qty",
            "CART_PRODUCT_PRICE": "Price",
            "CART_PRODUCT_UNIT_PRICE": "Unit price",
            "CART_PRODUCT_TOTAL_PRICE": "Total price",
            "CART_EMPTY": "The cart is empty",
            "HIKASHOP_TOTAL": "Total",
            "HIKASHOP_FINAL_TOTAL": "Final total",
            "PROCEED_TO_CHECKOUT": "Proceed to checkout",
            "REFRESH_CART": "Refresh cart",
            "PRODUCT_NOT_AVAILABLE": "The product %s is not available",
            "NOT_ENOUGH_STOCK_FOR_PRODUCT": "There is not enough stock for the product %s",
            "PRODUCT_NOT_YET_ON_SALE": "The product %s is not yet on sale",
            "PRODUCT_NOT_SOLD_ANYMORE": "The product %s is no longer on sale",
            "PRODUCT_SUCCESSFULLY_ADDED_TO_CART": "Product successfully added to the cart",
            "HIKASHOP_CHECKOUT_CART": "Cart",
            "HIKASHOP_CHECKOUT_CONFIRM": "Confirm",
            "HIKASHOP_CHECKOUT_STATUS": "Status",
            "HIKASHOP_CHECKOUT_SHIPPING": "Shipping",
            "HIKASHOP_CHECKOUT_PAYMENT": "Payment",
            "HIKASHOP_CHECKOUT_END": "End",
            "HIKASHOP_CHECKOUT_COUPON": "Coupon",
            "HIKASHOP_CHECKOUT_LOGIN": "Login",
            "HIKASHOP_CHECKOUT_ADDRESS": "Address",
            "NEXT": "Next",
            "CONTINUE_SHOPPING": "Continue shopping",
            "THANK_YOU_FOR_PURCHASE": "Thank you for your purchase.",
            "ORDER_IS_COMPLETE": "Your order is now complete.",
            "CURRENCY_NOT_ACCEPTED_FOR_PAYMENT": "The currency you selected is not accepted for payments",
            "PLEASE_ACCEPT_TERMS_BEFORE_FINISHING_ORDER": "Please accept the Terms and Conditions before proceeding",
            "PLEASE_ACCEPT_TERMS": "Please accept the Terms and Conditions before proceeding",
            "ADDITIONAL_INFORMATION": "Additional information",
            "LOGIN_OR_REGISTER_ACCOUNT": "Login or create a new account",
            "REGISTRATION_NOT_ALLOWED": "Registration not allowed",
            "WHEN_CLICKING_ACTIVATION": "Upon clicking on the activation link, your account will be activated and you will be able to continue your order",
            "PASSWORDS_DO_NOT_MATCH": "Passwords do not match",
            "VALID_EMAIL": "Please enter a valid e-mail address",
            "…": "4480 more keys, trimmed for the example"
        }
    },
    "meta": null,
    "error": null
}

The shop's languages

GET /languages read since 6.6.0

Which languages the shop publishes, and whether content translation is switched on at all.

When enabled is false the shop is single-language and the translation routes have nothing to do. The default language is marked and comes last, because it is the one already being edited on the main form rather than a translation of it.

Response

FieldTypeDescription
enabledbooleanWhether this shop translates its content. False on a single-language site.
languagesobject[]The published languages.
idintegerThe language id in the shop's table.
codestringThe tag, such as fr-FR.
shortcodestringThe lower-case underscored form, such as fr_fr, which is what the translation tables key on.
site_defaultbooleanTrue for the language the shop is written in. Its text is the original, not a translation.
GET/languages
curl "$SHOP/hikashop-api/v1/languages" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "enabled": true,
        "languages": [
            {
                "id": 2,
                "code": "fr-FR",
                "shortcode": "fr_fr",
                "site_default": false
            },
            {
                "id": 1,
                "code": "en-GB",
                "shortcode": "en_gb",
                "site_default": true
            }
        ]
    },
    "meta": null,
    "error": null
}

Read one product

GET /products/{id} read since 6.6.0

Everything the product editor needs in one call: the record, its prices, its images and files, its categories, its characteristics and variants, and the definitions of the custom fields that apply to it.

Ask for a variant's id and you get the variant, with parent_id set. A parent's variants are listed in full, so you rarely need to.

Path

ParameterTypeDescription
idrequiredintegerA parent product or a variant.

Response

FieldTypeDescription
idinteger
namestring
codestringThe SKU. Unique within the shop.
descriptionstringThe long description, as HTML.
description_typestringWhich editor the description was written with.
publishedboolean
quantityinteger-1 when this product does not track stock, which is not the same as 0.
msrpnumberThe manufacturer's suggested price, shown struck through when the shop is configured to.
gtinstringThe barcode: EAN, UPC or ISBN. This is what GET /products/lookup matches on.
conditionstringNew, used, refurbished. Used by the feeds rather than by the shop itself.
weightnumberShipping weight, in weight_unit.
weight_unitstringkg, g, lb or oz.
widthnumberIn dimension_unit.
heightnumberIn dimension_unit.
lengthnumberIn dimension_unit.
dimension_unitstringm, cm, mm, ft or in.
min_per_orderintegerThe smallest quantity a customer may order, 0 for no minimum.
max_per_orderintegerThe largest, 0 for no maximum.
sale_startinteger|nullUnix timestamp before which the product is not on sale.
sale_endinteger|nullUnix timestamp after which it is no longer sold.
page_titlestringSEO title, empty to use the name.
meta_descriptionstringSEO description.
keywordsstringSEO keywords.
canonicalstringA canonical URL, when this page should point at another.
urlstringThe address of the product page on the shop.
aliasstringThe slug used in that address.
accessobjectWho may see the product: mode (all, none or groups) and groups, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User group ids, meaningful only when the mode is groups.
contactbooleanWhether this product is enquired about rather than bought.
warehouse_idintegerThe warehouse holding the stock, 0 when the shop has none.
typestringmain for a product, variant for one of its variants.
parent_idintegerThe parent product when this is a variant, 0 otherwise.
value_idsinteger[]For a variant, the characteristic values it stands for.
manufacturer_idintegerThe brand, 0 when unset.
manufacturer_namestringIts name, saving a second call.
tax_idintegerThe tax category, 0 when the product is untaxed.
tax_namestringIts name.
tax_ratenumberThe rate as a fraction, so 0.2 is twenty percent.
pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
idinteger
valuenumberTax excluded, as stored.
currency_idinteger
min_quantityintegerFrom how many items this row applies, which is how quantity breaks are expressed.
accessobjectWho this price is for, in the same shape as the product access.
modestringAs above.
groupsinteger[]As above.
usersinteger[]Named customers, empty for everyone.
zone_idsinteger[]Zones this price applies in, empty for everywhere.
start_dateinteger|nullUnix timestamp.
end_dateinteger|nullUnix timestamp.
imagesobject[]In the order the editor shows them; the first is the main image.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderinginteger
descriptionstringThe alt text.
accessobjectWho may see it.
modestringAs above.
groupsinteger[]As above.
free_downloadbooleanFiles only; meaningless on an image.
filesobject[]Downloadable files, in the same shape as the images.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderinginteger
descriptionstring
accessobjectWho may download it.
free_downloadbooleanWhether it can be downloaded without buying the product.
categoriesobject[]The categories the product is in.
idinteger
namestring
bundleobject[]The products this one is made of, when it is a bundle.
idinteger
namestring
codestring
quantityintegerHow many of it the bundle contains.
optionsobject[]Products offered as options alongside this one.
idinteger
namestring
codestring
quantityinteger
relatedobject[]Products shown as related.
idinteger
namestring
codestring
quantityinteger
tagsinteger[]CMS tag ids.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
idintegerThe characteristic, such as Size.
namestringIts name.
valuesobject[]The values of it this product uses, such as S, M and L.
idintegerThe value id, which is what a variant refers to.
valuestringIts name, such as M.
variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
idintegerThe variant is a product in its own right, and this is its id.
codestringIts own SKU.
quantityintegerIts own stock. This is the figure to change, not the parent's.
publishedboolean
pricenumber|nullnull when the variant has no price of its own and the parent's applies.
valuesobject[]Which characteristic values this variant stands for, one per characteristic.
option_idintegerThe characteristic.
option_namestringIts name, so the variant can be labelled without a second call.
value_idintegerThe value.
valuestringIts name.
imagesobject[]The variant's own images, in the same shape as the product's.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderinginteger
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
namekeystringThe key used in custom_fields.
typestringtext, radio, singledropdown, file, and the rest of HikaShop's field types.
raw_typestringHikaShop's own name for the type, before it is mapped to something a client can render.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save the product without it.
optionsobject[]The choices, for a field that has them. Empty for a free text one.
multiplebooleanWhether more than one choice may be selected.
translatablebooleanWhether its value can be translated, which is what the translation routes offer.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts, comma separated. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
custom_fieldsobjectTheir values, keyed by namekey. The keys are whatever this shop has configured; fields in the same response says what they are.
custom_field_filesobjectFor custom fields holding a file, the file behind each value. Keyed the same way.
GET/products/{id}
curl "$SHOP/hikashop-api/v1/products/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 8645,
        "name": "Merino Socks — Pair",
        "code": "DEMO-0012",
        "description": "A socks we have carried since the shop opened, and still the one we use ourselves.",
        "description_type": "",
        "published": true,
        "quantity": 46,
        "msrp": 0,
        "gtin": "",
        "condition": "",
        "weight": 1.545,
        "weight_unit": "kg",
        "width": 0,
        "height": 0,
        "length": 0,
        "dimension_unit": "m",
        "min_per_order": 0,
        "max_per_order": 0,
        "sale_start": 0,
        "sale_end": 0,
        "page_title": "",
        "meta_description": "",
        "keywords": "",
        "canonical": "",
        "url": "",
        "alias": "",
        "access": {
            "mode": "all",
            "groups": []
        },
        "contact": false,
        "warehouse_id": 0,
        "type": "main",
        "parent_id": 0,
        "value_ids": [],
        "manufacturer_id": 0,
        "manufacturer_name": "",
        "tax_id": 0,
        "tax_name": "",
        "tax_rate": 0,
        "prices": [
            {
                "id": 4641,
                "value": 108.92,
                "currency_id": 1,
                "min_quantity": 0,
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "users": [],
                "zone_ids": [],
                "start_date": 0,
                "end_date": 0
            }
        ],
        "images": [
            {
                "id": 7767,
                "name": "Merino Socks — Pair",
                "path": "demo-0012.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012.png",
                "ordering": 1,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            },
            {
                "id": 7768,
                "name": "Merino Socks — Pair",
                "path": "demo-0012-2.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-2.png",
                "ordering": 2,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            },
            {
                "id": 7769,
                "name": "Merino Socks — Pair",
                "path": "demo-0012-3.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-3.png",
                "ordering": 3,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            }
        ],
        "files": [],
        "categories": [
            {
                "id": 237,
                "name": "Wallets"
            }
        ],
        "bundle": [],
        "options": [],
        "related": [],
        "tags": [],
        "characteristics": [
            {
                "id": 8,
                "name": "Size",
                "values": [
                    {
                        "id": 9,
                        "value": "S"
                    },
                    {
                        "id": 10,
                        "value": "L"
                    },
                    {
                        "id": 21,
                        "value": "M"
                    },
                    {
                        "id": 35,
                        "value": "XS"
                    },
                    "… 1 more, trimmed for the example"
                ]
            }
        ],
        "variants": [
            {
                "id": 8646,
                "code": "DEMO-0012-XS",
                "quantity": 11,
                "published": true,
                "price": null,
                "values": [
                    {
                        "option_id": 8,
                        "option_name": "Size",
                        "value_id": 35,
                        "value": "XS"
                    }
                ],
                "images": [
                    {
                        "id": 7770,
                        "name": "Merino Socks — Pair — XS",
                        "path": "demo-0012-xs.png",
                        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-xs.png",
                        "ordering": 1
                    }
                ]
            },
            {
                "id": 8647,
                "code": "DEMO-0012-S",
                "quantity": 0,
                "published": true,
                "price": null,
                "values": [
                    {
                        "option_id": 8,
                        "option_name": "Size",
                        "value_id": 9,
                        "value": "S"
                    }
                ],
                "images": [
                    {
                        "id": 7771,
                        "name": "Merino Socks — Pair — S",
                        "path": "demo-0012-s.png",
                        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-s.png",
                        "ordering": 1
                    }
                ]
            },
            {
                "id": 8648,
                "code": "DEMO-0012-M",
                "quantity": 21,
                "published": true,
                "price": null,
                "values": [
                    {
                        "option_id": 8,
                        "option_name": "Size",
                        "value_id": 21,
                        "value": "M"
                    }
                ],
                "images": [
                    {
                        "id": 7772,
                        "name": "Merino Socks — Pair — M",
                        "path": "demo-0012-m.png",
                        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-m.png",
                        "ordering": 1
                    }
                ]
            },
            {
                "id": 8649,
                "code": "DEMO-0012-L",
                "quantity": 1,
                "published": true,
                "price": null,
                "values": [
                    {
                        "option_id": 8,
                        "option_name": "Size",
                        "value_id": 10,
                        "value": "L"
                    }
                ],
                "images": [
                    {
                        "id": 7773,
                        "name": "Merino Socks — Pair — L",
                        "path": "demo-0012-l.png",
                        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-l.png",
                        "ordering": 1
                    }
                ]
            },
            "… 1 more, trimmed for the example"
        ],
        "fields": [
            {
                "namekey": "test_ajax_image",
                "type": "ajaximage",
                "raw_type": "ajaximage",
                "label": "Test Ajax Image",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": "%Y-%m-%d"
            },
            {
                "namekey": "product_gtin",
                "type": "text",
                "raw_type": "text",
                "label": "GTIN",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            }
        ],
        "custom_fields": {
            "test_ajax_image": null,
            "product_gtin": null
        },
        "custom_field_files": {
            "test_ajax_image": []
        }
    },
    "meta": null,
    "error": null
}

Resolve a scanned barcode

GET /products/lookup read since 6.6.0

One product from one barcode, for a scanner. It matches the product code and the GTIN, and it looks at variants as well as parents, because the thing with a barcode on it is usually the variant.

Exactly one product comes back, or not_found. It is deliberately not a search: a scanner needs an answer, not a list.

Query

ParameterTypeDescription
barcoderequiredstringWhat the scanner read. Matched against the product code and the GTIN.

Response

FieldTypeDescription
idintegerThe product to open. For a variant, this is its parent.
variant_idintegerThe variant that was scanned, 0 when the barcode belonged to the parent.
namestring
codestringThe SKU that matched.
gtinstringThe barcode held on the record, which may differ in leading zeros from what was scanned.
quantityintegerThe stock of whichever record matched, so a stock take needs no second call.

Errors

CodeHTTPMeans
missing_barcode400No barcode was given.
not_found404Nothing in the shop carries that code.
GET/products/lookup?barcode=TEST{id}
curl "$SHOP/hikashop-api/v1/products/lookup?barcode=TEST{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 1,
        "variant_id": 0,
        "name": "Test Product (Vendor 2)",
        "code": "TEST001",
        "gtin": "",
        "quantity": -1
    },
    "meta": null,
    "error": null
}

Products running out

GET /products/low-stock read since 6.6.0

Sellable items at or below a stock threshold, cheapest first to reorder. Variants are listed in their own right, since that is where the stock actually sits.

Products that do not track stock are left out: they are not running out of anything.

Query

ParameterTypeDescription
thresholdintegerAt or below which a product counts as low. Defaults to 5.
limitintegerDefaults to 20, capped at 100.

Response a list

FieldTypeDescription
idintegerThe parent product.
variant_idintegerThe variant that is low, 0 when the parent itself is.
namestring
codestringThe SKU of whichever record is low.
quantityintegerWhat is left.
GET/products/low-stock?threshold={id}&limit=3
curl "$SHOP/hikashop-api/v1/products/low-stock?threshold={id}&limit=3" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 8657,
            "variant_id": 0,
            "name": "Botanical Shampoo Bottle — 500 ml",
            "code": "DEMO-0019",
            "quantity": 0
        },
        {
            "id": 8896,
            "variant_id": 0,
            "name": "Brass Colored Pencils — 12",
            "code": "DEMO-0193",
            "quantity": 0
        },
        {
            "id": 9007,
            "variant_id": 0,
            "name": "Brass Colored Pencils — 36",
            "code": "DEMO-0279",
            "quantity": 0
        }
    ],
    "meta": null,
    "error": null
}

Set a product's stock

POST /products/{id}/stock write since 6.6.0

Sets the tracked quantity to an absolute figure, which is what a stock take does. It is not an adjustment: send what the shelf holds, not the difference.

A product with variants keeps no stock of its own, so set it on the variant instead. Asking to set it on the parent is refused rather than silently ignored.

Path

ParameterTypeDescription
idrequiredintegerThe product or variant to set.

Body

FieldTypeDescription
quantityrequiredintegerThe new absolute quantity. -1 turns stock tracking off for this product.

Response

FieldTypeDescription
idinteger
quantityintegerAs stored, so you can confirm what was written.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
has_variants409It is a parent: set the stock on one of its variants.
POST/products/2/stock
curl -X POST "$SHOP/hikashop-api/v1/products/2/stock" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 42
}'
200success
{
    "data": {
        "id": 2,
        "quantity": 42
    },
    "meta": null,
    "error": null
}

List customers

GET /customers read since 6.6.0

The customers the operator may see, with enough to recognise one and to know whether they have bought anything.

Guests are included. A shop that lets people order without an account still has a row for each of them, and type is how you tell the two apart.

Query

ParameterTypeDescription
startintegerOffset. Defaults to 0.
limitintegerDefaults to 20, capped at 100.
searchstringMatches the email address, the account name and login, and the first name and surname on any of their addresses, including the two together, so John Doe finds a guest who has never had an account.

Response a list

FieldTypeDescription
idintegerThe HikaShop customer id, which is not the CMS user id.
namestringThe account name, or for a guest the name on their default address, since a guest has no account to take one from.
emailstring
typestringregistered for an account, guest for someone who ordered without one.
createdintegerUnix timestamp of the first time the shop saw them.
order_countintegerHow many orders they have placed, so a list can be sorted by worth without a second call.

Envelope meta

FieldTypeDescription
startintegerEchoes the offset used.
limitintegerEchoes the page size used.
totalintegerCustomers matching the filter, before paging.
GET/customers?limit=2
curl "$SHOP/hikashop-api/v1/customers?limit=2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 5272,
            "name": "E2E Tester",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
            "type": "guest",
            "created": 1786354936,
            "order_count": 0
        },
        {
            "id": 5271,
            "name": "E2E Tester",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
            "type": "guest",
            "created": 1786353021,
            "order_count": 0
        }
    ],
    "meta": {
        "start": 0,
        "limit": 2,
        "total": 336
    },
    "error": null
}

List discounts and coupons

GET /discounts read since 6.6.0

Both kinds of reduction live in one table and are told apart by type: a discount applies by itself when its conditions are met, a coupon waits for its code to be entered.

The restriction fields are the interesting part, and they are all lists of ids: which products, which categories, which zones, which customers, and the same again for exclusions. An empty list means no restriction of that kind rather than none allowed.

Query

ParameterTypeDescription
startintegerOffset. Defaults to 0.
limitintegerDefaults to 20, capped at 100.
typestringdiscount or coupon, to list one kind.
searchstringMatches the code.

Response a list

FieldTypeDescription
idinteger
typestringdiscount applies by itself, coupon waits for its code.
codestringWhat the customer types. Empty on a discount.
kindstringWhether the value is a percentage or a fixed amount.
valuenumberThe reduction, read according to kind.
currency_idintegerThe currency a fixed amount is in.
publishedboolean
startinteger|nullUnix timestamp before which it does not apply.
endinteger|nullUnix timestamp after which it expires.
minimum_ordernumberOrder total below which it does not apply, 0 for none.
maximum_ordernumberOrder total above which it stops applying, 0 for none.
quotaintegerHow many times it may be used in total, 0 for no limit.
quota_per_userintegerHow many times one customer may use it, 0 for no limit.
used_timesintegerHow many times it already has been.
tax_includedbooleanWhether the value is understood as tax included.
tax_idintegerThe tax category of the reduction itself.
shipping_percentnumberA reduction on the shipping rather than on the goods.
minimum_productsintegerFewest items in the cart for it to apply.
maximum_productsintegerMost items for it to still apply.
product_idsinteger[]Restricted to these products. Empty means all of them.
exclude_product_idsinteger[]Never applies to these.
category_idsinteger[]Restricted to these categories.
category_childsbooleanWhether those categories include their sub-categories.
exclude_category_idsinteger[]Never applies in these categories.
exclude_category_childsbooleanWhether those exclusions include sub-categories.
zone_idsinteger[]Restricted to these zones.
user_idsinteger[]Restricted to these customers.
accessobjectWhich user groups it is for, in the usual mode and groups shape.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
exclude_accessobjectWhich user groups it is never for.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
auto_loadbooleanFor a coupon, whether the shop applies it without the customer typing it.
product_onlybooleanWhether it reduces only the goods and leaves the fees alone.
discounted_productsintegerHow many products in the cart it applied to, on a discount that has been used.

Envelope meta

FieldTypeDescription
startintegerEchoes the offset used.
limitintegerEchoes the page size used.
totalintegerRows matching the filter, before paging.
GET/discounts?limit=2
curl "$SHOP/hikashop-api/v1/discounts?limit=2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 435,
            "type": "discount",
            "code": "auto-2",
            "kind": "percent",
            "value": 9,
            "currency_id": 0,
            "published": true,
            "start": 0,
            "end": 0,
            "minimum_order": 0,
            "maximum_order": 0,
            "quota": 0,
            "quota_per_user": 0,
            "used_times": 0,
            "tax_included": false,
            "tax_id": 0,
            "shipping_percent": 0,
            "minimum_products": 0,
            "maximum_products": 0,
            "product_ids": [],
            "exclude_product_ids": [],
            "category_ids": [],
            "category_childs": false,
            "exclude_category_ids": [],
            "exclude_category_childs": false,
            "zone_ids": [],
            "user_ids": [],
            "access": {
                "mode": "all",
                "groups": []
            },
            "exclude_access": {
                "mode": "none",
                "groups": []
            },
            "auto_load": false,
            "product_only": false,
            "discounted_products": 0
        },
        {
            "id": 434,
            "type": "coupon",
            "code": "E2E1786215747620",
            "kind": "percent",
            "value": 12,
            "currency_id": 0,
            "published": true,
            "start": 0,
            "end": 0,
            "minimum_order": 0,
            "maximum_order": 0,
            "quota": 0,
            "quota_per_user": 0,
            "used_times": 0,
            "tax_included": false,
            "tax_id": 0,
            "shipping_percent": 0,
            "minimum_products": 0,
            "maximum_products": 0,
            "product_ids": [],
            "exclude_product_ids": [],
            "category_ids": [],
            "category_childs": false,
            "exclude_category_ids": [],
            "exclude_category_childs": false,
            "zone_ids": [],
            "user_ids": [],
            "access": {
                "mode": "all",
                "groups": []
            },
            "exclude_access": {
                "mode": "none",
                "groups": []
            },
            "auto_load": false,
            "product_only": false,
            "discounted_products": 0
        }
    ],
    "meta": {
        "start": 0,
        "limit": 2,
        "total": 5
    },
    "error": null
}

The category tree

GET /categories read since 6.6.0

One level of the category tree at a time, unpublished categories included, which is what makes this a management view rather than a shop one.

Pass a parent_id to walk down. has_children tells you whether there is anything below without asking, so a tree can be drawn lazily.

HikaShop keeps its manufacturers and its tax categories in the same table as the product categories, told apart by their root. Ask for the tree you want.

Query

ParameterTypeDescription
parent_idintegerWhose children to list. Omit for the top level.
startintegerOffset. Defaults to 0.
limitintegerDefaults to 20, capped at 100.
searchstringMatches the name, across the whole tree rather than one level.

Response a list

FieldTypeDescription
idinteger
namestring
parent_idintegerIts parent, so a flat answer can be rebuilt into a tree.
publishedboolean
has_childrenbooleanWhether anything sits below it.
imagestring|nullAbsolute URL of its image, null when it has none.

Envelope meta

FieldTypeDescription
totalintegerCategories matching, before paging.
startintegerEchoes the offset used.
limitintegerEchoes the page size used.
GET/categories?limit=3
curl "$SHOP/hikashop-api/v1/categories?limit=3" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 218,
            "name": "Bags & Wear",
            "parent_id": 2,
            "published": true,
            "has_children": true,
            "image": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-cat-218.png"
        },
        {
            "id": 220,
            "name": "Beauty",
            "parent_id": 2,
            "published": true,
            "has_children": true,
            "image": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-cat-220.png"
        },
        {
            "id": 194,
            "name": "Boots",
            "parent_id": 2,
            "published": true,
            "has_children": true,
            "image": ""
        }
    ],
    "meta": {
        "total": 20,
        "start": 0,
        "limit": 3
    },
    "error": null
}

Read one category

GET /categories/{id} read since 6.6.0

A category with its description, its image, who may see it, and your own category fields.

Path

ParameterTypeDescription
idrequiredintegerThe category id.

Response

FieldTypeDescription
fieldsobject[]The definitions of your own category fields.
idinteger
namestring
parent_idintegerIts parent.
typestringWhich tree it belongs to: product, manufacturer, tax, and so on.
descriptionstringThe long description, as HTML.
meta_descriptionstringSEO description.
publishedboolean
accessobjectWho may see it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
imagestring|nullAbsolute URL of its image, null when it has none.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.
GET/categories/2
curl "$SHOP/hikashop-api/v1/categories/2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "fields": [],
        "id": 2,
        "name": "product category",
        "parent_id": 1,
        "type": "product",
        "description": "",
        "meta_description": "",
        "published": true,
        "access": {
            "mode": "all",
            "groups": []
        },
        "image": "",
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

The shop's own bulk operations

GET /massactions read since 6.6.0

The mass actions the merchant has configured for a listing, so a client can offer the merchant's own bulk operations rather than a fixed set of its own.

Whatever they built in the backend appears here, and running one is POST /massactions/{id}.

Query

ParameterTypeDescription
tablerequiredstringWhich listing: product, order, user, category, address.

Response a list

FieldTypeDescription
idintegerWhat to run.
namestringAs the merchant named it.
descriptionstringTheir own note about what it does, when they wrote one.
tablestringThe listing it belongs to.
restrictedbooleanWhether it may only run on a selection rather than on everything matching a filter.

Errors

CodeHTTPMeans
invalid_request400The table is not one the shop has mass actions for.
forbidden403The operator may not view that kind of record.
GET/massactions?table=product
curl "$SHOP/hikashop-api/v1/massactions?table=product" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 2,
            "name": "Unpublish selected",
            "description": "Takes the chosen products off sale.",
            "table": "product",
            "restricted": false
        }
    ],
    "meta": null,
    "error": null
}

Browse the upload folder

GET /media/browse read since 6.6.0

Folders and images inside the shop's upload folder, so an existing image can be picked instead of uploaded again.

Paths are relative to that folder and never escape it: a path pointing outside is refused rather than resolved.

Query

ParameterTypeDescription
folderstringWhich folder to list, relative to the upload folder. Omit for its root.
offsetintegerOffset into the images, for a folder with many.

Response

FieldTypeDescription
folderstringThe folder being listed.
parentstringThe folder above, for walking back up.
has_parentbooleanFalse at the root, where there is nothing above.
foldersobject[]The folders inside it.
imagesobject[]The images inside it.
namestringThe file name.
pathstringRelative to the upload folder, which is what a product image stores.
urlstringAbsolute, ready to display.
totalintegerHow many images the folder holds in all.
offsetintegerEchoes the offset used.

Errors

CodeHTTPMeans
not_found404No such folder, or a path that tried to leave the upload folder.
GET/media/browse
curl "$SHOP/hikashop-api/v1/media/browse" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "folder": "",
        "parent": "",
        "has_parent": false,
        "folders": [],
        "images": [
            {
                "name": "demo-0001.png",
                "path": "demo-0001.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0001.png"
            },
            {
                "name": "demo-0002-2.png",
                "path": "demo-0002-2.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0002-2.png"
            },
            {
                "name": "demo-0002.png",
                "path": "demo-0002.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0002.png"
            },
            {
                "name": "demo-0003.png",
                "path": "demo-0003.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0003.png"
            },
            "… 579 more, trimmed for the example"
        ],
        "total": 583,
        "offset": 0
    },
    "meta": null,
    "error": null
}

Search countries, states and zones

GET /zones read since 6.6.0

The shop's zones, which are its countries, its states and any grouping the merchant made of them. Meant for filling a picker: search for a name, or resolve ids you already hold.

An address stores a zone by its namekey rather than its id, which is why both come back.

Query

ParameterTypeDescription
searchstringMatches the name.
idsstringComma separated zone ids, to resolve a known set.
typestringRestrict to country, state or a zone group.

Response a list

FieldTypeDescription
idinteger
namekeystringWhat an address stores, such as FRA or US-CA.
namestringTranslated where the shop has a translation for it.
typestringcountry, state, or the kind of grouping it is.
GET/zones?search=fra
curl "$SHOP/hikashop-api/v1/zones?search=fra" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 73,
            "namekey": "country_France_73",
            "name": "France",
            "type": "country"
        },
        {
            "id": 2195,
            "namekey": "state_Al_Jufrah_2195",
            "name": "Al Jufrah",
            "type": "state"
        },
        {
            "id": 2196,
            "namekey": "state_Al_Kufrah_2196",
            "name": "Al Kufrah",
            "type": "state"
        },
        {
            "id": 1631,
            "namekey": "state_Francisco_Moraz__n_1631",
            "name": "Francisco Morazán",
            "type": "state"
        },
        "… 4 more, trimmed for the example"
    ],
    "meta": null,
    "error": null
}

Search customers for a picker

GET /users read since 6.6.0

A short list of customers matching a search, for the pickers that restrict a price or a discount to named people. GET /customers is the listing to use for anything else.

Query

ParameterTypeDescription
searchstringMatches the name and the email address.
idsstringComma separated ids, to resolve a known set.

Response a list

FieldTypeDescription
idintegerThe customer id, which is what a restriction stores.
namestring
emailstringEnough to tell two people of the same name apart.
GET/users?search=a
curl "$SHOP/hikashop-api/v1/users?search=a" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 5272,
            "name": "",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
        },
        {
            "id": 20,
            "name": "",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
        },
        {
            "id": 4939,
            "name": "",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
        },
        {
            "id": 4940,
            "name": "",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
        },
        "… 26 more, trimmed for the example"
    ],
    "meta": null,
    "error": null
}

Reference data for the product editor

GET /products/meta read since 6.6.0

Everything a product form needs to offer choices: the currencies with their formatting, the tax categories, the characteristics and their values, the units, the field definitions, the warehouses and the tags.

One call rather than eight, and it changes rarely, so cache it.

Response

FieldTypeDescription
currenciesobject[]Every published currency, with enough to format an amount the way the shop does.
idintegerWhat a price stores.
codestringThe ISO code, such as EUR.
symbolstring
namestring
decimalsintegerHow many decimal places to show.
decimal_sepstringThe decimal separator.
thousands_sepstringThe thousands separator.
symbol_beforebooleanWhether the symbol goes before the figure.
spacebooleanWhether a space separates the symbol from the figure.
rounding_incrementnumberWhat amounts are rounded to, for a currency without small coins.
main_currency_idintegerThe shop's own currency.
tax_categoriesobject[]The tax categories a product can be put in.
idintegerWhat tax_id on a product stores.
namestring
parent_idintegerThey live in the category tree, so they have a parent like anything else there.
characteristicsobject[]Every characteristic in the shop, with its values, for building variants.
idinteger
namestring
valuesobject[]Its values.
idintegerWhat a variant refers to.
valuestringIts name, such as M.
weight_unitsstring[]The weight units the shop accepts, such as kg.
dimension_unitsstring[]The dimension units the shop accepts, such as m.
product_fieldsobject[]The definitions of your own product fields.
namekeystringThe key its value is stored under.
typestringWhat to render: text, radio, singledropdown, file, and the rest.
raw_typestringHikaShop's own name for the type.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save without it.
optionsobject[]The choices, for a field that has them.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanWhether its value can be translated.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
category_fieldsobject[]The definitions of your own category fields.
namekeystringThe key its value is stored under.
labelstringTranslated into the operator's language.
typestringWhat to render: text, radio, singledropdown, file, and the rest.
raw_typestringHikaShop's own name for the type.
requiredbooleanWhether the shop refuses to save without it.
defaultstringThe value used when none is given.
optionsobject[]The choices, for a field that has them.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanWhether its value can be translated.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
bundle_supportedbooleanWhether this edition can sell bundles.
warehousesobject[]The warehouses stock can be held in. Empty when the shop has none.
idintegerWhat warehouse_id on a product stores.
namestring
tagsobject[]The CMS tags a product can carry.
idinteger
namestring
parent_idintegerTags are a tree in both Joomla and WordPress.
GET/products/meta
curl "$SHOP/hikashop-api/v1/products/meta" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "currencies": [
            {
                "id": 1,
                "code": "EUR",
                "symbol": "€",
                "name": "Euro",
                "decimals": 2,
                "decimal_sep": ",",
                "thousands_sep": ".",
                "symbol_before": false,
                "space": true,
                "rounding_increment": 0
            },
            {
                "id": 2,
                "code": "USD",
                "symbol": "$",
                "name": "United States dollar",
                "decimals": 2,
                "decimal_sep": ".",
                "thousands_sep": ",",
                "symbol_before": true,
                "space": false,
                "rounding_increment": 0
            }
        ],
        "main_currency_id": 1,
        "tax_categories": [
            {
                "id": 11,
                "name": "Default tax category",
                "parent_id": 3
            },
            {
                "id": 3,
                "name": "taxation category",
                "parent_id": 1
            }
        ],
        "characteristics": [
            {
                "id": 1,
                "name": "Vendor",
                "values": []
            },
            {
                "id": 5,
                "name": "Color",
                "values": [
                    {
                        "id": 6,
                        "value": "Red"
                    },
                    {
                        "id": 7,
                        "value": "Blue"
                    }
                ]
            },
            {
                "id": 8,
                "name": "Size",
                "values": [
                    {
                        "id": 9,
                        "value": "S"
                    },
                    {
                        "id": 10,
                        "value": "L"
                    },
                    {
                        "id": 21,
                        "value": "M"
                    },
                    {
                        "id": 35,
                        "value": "XS"
                    },
                    "… 1 more, trimmed for the example"
                ]
            },
            {
                "id": 14,
                "name": "Colour",
                "values": [
                    {
                        "id": 15,
                        "value": "Charcoal"
                    },
                    {
                        "id": 16,
                        "value": "Navy"
                    },
                    {
                        "id": 17,
                        "value": "Oatmeal"
                    },
                    {
                        "id": 18,
                        "value": "Forest"
                    },
                    "… 2 more, trimmed for the example"
                ]
            },
            "… 1 more, trimmed for the example"
        ],
        "weight_units": [
            "kg",
            "g",
            "mg",
            "lb",
            "… 2 more, trimmed for the example"
        ],
        "dimension_units": [
            "m",
            "dm",
            "cm",
            "mm",
            "… 3 more, trimmed for the example"
        ],
        "product_fields": [
            {
                "namekey": "test_ajax_image",
                "type": "ajaximage",
                "raw_type": "ajaximage",
                "label": "Test Ajax Image",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": "%Y-%m-%d"
            },
            {
                "namekey": "product_gtin",
                "type": "text",
                "raw_type": "text",
                "label": "GTIN",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            }
        ],
        "category_fields": [],
        "bundle_supported": true,
        "warehouses": [
            {
                "id": 1,
                "name": "Default Warehouse"
            },
            {
                "id": 2,
                "name": "Main Warehouse (renamed)"
            }
        ],
        "tags": []
    },
    "meta": null,
    "error": null
}

Replace a product's prices

PUT /products/{id}/prices write since 6.6.0

Replaces the whole price set, it does not merge into it. Send every price the product should have, including the ones you are not changing; anything you leave out is deleted.

That is deliberate, because a price set is a set: quantity breaks and audience restrictions only make sense against each other.

Path

ParameterTypeDescription
idrequiredintegerThe product or variant.

Body

FieldTypeDescription
pricesrequiredobject[]The complete set. Each needs at least a value and a currency_id; min_quantity, access, users, zone_ids, start_date and end_date are optional and default to no restriction.

Response a list

FieldTypeDescription
idinteger
valuenumberTax excluded, as stored.
currency_idinteger
min_quantityintegerFrom how many items this row applies, which is how a quantity break is expressed.
accessobjectWhich user groups the price is for.
modestringall, none or groups.
groupsinteger[]User group ids, not view levels.
usersinteger[]Named customers, empty for everyone.
zone_idsinteger[]Zones it applies in, empty for everywhere.
start_dateinteger|nullUnix timestamp.
end_dateinteger|nullUnix timestamp.

Errors

CodeHTTPMeans
invalid_request400No prices array was sent.
invalid_price400A price in the set cannot be stored, and the message says which one and why. Nothing is saved: a set is replaced whole or not at all.
not_found404No such product, or the operator may not change it.
PUT/products/{id}/prices
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/prices" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
        {
            "value": 19.9,
            "currency_id": 1,
            "min_quantity": 1
        }
    ]
}'
200success
{
    "data": [
        {
            "id": 4930,
            "value": 19.9,
            "currency_id": 1,
            "min_quantity": 1,
            "access": {
                "mode": "all",
                "groups": []
            },
            "users": [],
            "zone_ids": [],
            "start_date": 0,
            "end_date": 0
        }
    ],
    "meta": null,
    "error": null
}

Replace a product's categories

PUT /products/{id}/categories write since 6.6.0

Replaces the set of categories the product is in. As with the prices, send the complete set: what you leave out is removed.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Body

FieldTypeDescription
categoriesrequiredinteger[]The complete set of category ids. An empty array takes the product out of every category, which hides it from the shop.

Response a list

FieldTypeDescription
idintegerThe category.
namestringIts name, so a client need not look it up.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
PUT/products/{id}/categories
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/categories" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "categories": [
        "230"
    ]
}'
200success
{
    "data": [
        {
            "id": 230,
            "name": "Planners"
        }
    ],
    "meta": null,
    "error": null
}

A product's translations

GET /products/{id}/translations read since 6.6.0

Which of a record's texts can be translated, what they say now in each language, and what the original says. enabled is false on a single-language shop, where there is nothing to do.

columns is the list of translatable things: HikaShop's own (the name, the description, the SEO texts) plus every custom field the merchant flagged translatable. It is built the same way the backend builds it, so what you are offered is exactly what the shop will store.

The labels and values of a custom field itself are website configuration and are not offered here; they belong in the backend.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Response

FieldTypeDescription
enabledbooleanFalse on a single-language shop, where the rest is empty.
languagesobject[]The shop's languages. The default one is marked and comes last, because its text is the original rather than a translation of it.
idinteger
codestringThe tag, such as fr-FR.
shortcodestringThe lower-case underscored form the translation tables key on.
site_defaultbooleanTrue for the language the shop is written in.
columnsobject[]What can be translated on this record: HikaShop's own texts plus every custom field flagged translatable.
namestringThe column name, such as product_name. This is the key to send a translation under.
typestringtext for a line, textarea for prose, so a client knows which control to draw.
valuesobjectWhat each language says now, keyed by language code and then by column. Keyed by language code, and within that by column name, both of which depend on the shop.
originalobjectWhat the record itself says, keyed by column, so a translator can see what they are translating. Keyed by column name, which depends on the fields this shop has.

Errors

CodeHTTPMeans
not_found404No such record, or the operator may not see it.
translation_disabled400This shop does not translate content.
GET/products/1/translations
curl "$SHOP/hikashop-api/v1/products/1/translations" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "enabled": true,
        "languages": [
            {
                "id": 2,
                "code": "fr-FR",
                "shortcode": "fr_fr",
                "site_default": false
            },
            {
                "id": 1,
                "code": "en-GB",
                "shortcode": "en_gb",
                "site_default": true
            }
        ],
        "columns": [
            {
                "name": "product_name",
                "type": "text"
            },
            {
                "name": "product_description",
                "type": "html"
            },
            {
                "name": "product_page_title",
                "type": "text"
            },
            {
                "name": "product_url",
                "type": "text"
            },
            "… 4 more, trimmed for the example"
        ],
        "original": {
            "product_name": "Test Product (Vendor 2)",
            "product_description": "Test product owned by vendor 2",
            "product_page_title": "",
            "product_url": "",
            "product_meta_description": "",
            "product_keywords": "",
            "product_alias": "",
            "product_canonical": ""
        },
        "values": []
    },
    "meta": null,
    "error": null
}

A category's translations

GET /categories/{id}/translations read since 6.6.0

The same as for a product, for a category: its name, its description, its SEO texts and its translatable custom fields.

Path

ParameterTypeDescription
idrequiredintegerThe category.

Response

FieldTypeDescription
enabledbooleanFalse on a single-language shop, where the rest is empty.
languagesobject[]The shop's languages. The default one is marked and comes last, because its text is the original rather than a translation of it.
idinteger
codestringThe tag, such as fr-FR.
shortcodestringThe lower-case underscored form the translation tables key on.
site_defaultbooleanTrue for the language the shop is written in.
columnsobject[]What can be translated on this record: HikaShop's own texts plus every custom field flagged translatable.
namestringThe column name, such as product_name. This is the key to send a translation under.
typestringtext for a line, textarea for prose, so a client knows which control to draw.
valuesobjectWhat each language says now, keyed by language code and then by column. Keyed by language code, and within that by column name, both of which depend on the shop.
originalobjectWhat the record itself says, keyed by column, so a translator can see what they are translating. Keyed by column name, which depends on the fields this shop has.

Errors

CodeHTTPMeans
not_found404No such record, or the operator may not see it.
translation_disabled400This shop does not translate content.
GET/categories/2/translations
curl "$SHOP/hikashop-api/v1/categories/2/translations" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "enabled": true,
        "languages": [
            {
                "id": 2,
                "code": "fr-FR",
                "shortcode": "fr_fr",
                "site_default": false
            },
            {
                "id": 1,
                "code": "en-GB",
                "shortcode": "en_gb",
                "site_default": true
            }
        ],
        "columns": [
            {
                "name": "category_name",
                "type": "text"
            },
            {
                "name": "category_description",
                "type": "html"
            },
            {
                "name": "category_product_page_info",
                "type": "html"
            },
            {
                "name": "category_page_title",
                "type": "text"
            },
            "… 4 more, trimmed for the example"
        ],
        "original": {
            "category_name": "product category",
            "category_description": "",
            "category_product_page_info": "",
            "category_page_title": "",
            "category_meta_description": "",
            "category_keywords": "",
            "category_alias": "",
            "category_canonical": ""
        },
        "values": []
    },
    "meta": null,
    "error": null
}

Save a product's translations

PUT /products/{id}/translations write since 6.6.0

Writes the translations of one record. Send only the languages you changed; the rest are left alone.

Where the shop uses Falang, the values go into its tables. Where it does not, they become language overrides keyed on the original text, which is why renaming a product re-keys its translations rather than losing them.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Body

An object keyed by language, each holding an object of column name to text:

{ "2": { "product_name": "Lampe de bureau", "product_description": "…" } }

The keys are the language ids from languages in the GET, and the column names are the ones from columns. A language you do not send is left alone, and so is a column you do not send within a language you do.

Response

FieldTypeDescription
idintegerThe record that was saved.
savedintegerHow many languages were written, so a client can tell that something was actually stored.

Errors

CodeHTTPMeans
not_found404No such record, or the operator may not change it.
translation_disabled400This shop does not translate content.
PUT/products/1/translations
curl -X PUT "$SHOP/hikashop-api/v1/products/1/translations" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "2": {
        "product_name": "Lampe de bureau"
    }
}'
200success
{
    "data": {
        "id": 1,
        "saved": 1
    },
    "meta": null,
    "error": null
}

Save a category's translations

PUT /categories/{id}/translations write since 6.6.0

The same as for a product, for a category.

Path

ParameterTypeDescription
idrequiredintegerThe category.

Body

An object keyed by language, each holding an object of column name to text:

{ "2": { "product_name": "Lampe de bureau", "product_description": "…" } }

The keys are the language ids from languages in the GET, and the column names are the ones from columns. A language you do not send is left alone, and so is a column you do not send within a language you do.

Response

FieldTypeDescription
idintegerThe record that was saved.
savedintegerHow many languages were written.

Errors

CodeHTTPMeans
not_found404No such record, or the operator may not change it.
translation_disabled400This shop does not translate content.
PUT/categories/2/translations
curl -X PUT "$SHOP/hikashop-api/v1/categories/2/translations" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "2": {
        "category_name": "Sacs et v\u00eatements"
    }
}'
200success
{
    "data": {
        "id": 2,
        "saved": 1
    },
    "meta": null,
    "error": null
}

Dashboard figures

GET /stats/dashboard read since 6.6.0

Revenue, orders, average basket and new customers over a period, the same figures again for the period before it so a change can be shown, the series behind them, and the best sellers.

Only orders the shop counts as sold are included, so a cancelled order does not inflate a total. Amounts are in the shop's own currency.

Query

ParameterTypeDescription
rangestringday, week, month or year. Defaults to month.

Response

FieldTypeDescription
rangestringThe period the figures cover.
currency_idintegerThe shop's currency, which every amount here is in.
totalsobjectThe four headline figures.
revenuenumberTaken in the period.
ordersintegerHow many were placed.
average_ordernumberRevenue divided by orders.
customersintegerNew customers in the period.
previousobjectThe same four figures for the preceding period of the same length, for a comparison.
revenuenumberTaken in the preceding period.
ordersintegerPlaced in it.
average_ordernumberIts average basket.
customersintegerNew customers in it.
series_granularitystringWhether the series is by day, week, month or year, which follows from the range.
revenue_seriesobject[]One point per interval, for a chart.
datestringThe interval, as a date.
revenuenumberTaken in it.
top_productsobject[]The best sellers of the period.
namestring
quantityintegerHow many were sold.
GET/stats/dashboard?range=month
curl "$SHOP/hikashop-api/v1/stats/dashboard?range=month" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "range": "month",
        "currency_id": 1,
        "totals": {
            "revenue": 10724.83,
            "orders": 39,
            "average_order": 275,
            "customers": 30
        },
        "previous": {
            "revenue": 13450.9,
            "orders": 39,
            "average_order": 344.89,
            "customers": 32
        },
        "series_granularity": "day",
        "revenue_series": [
            {
                "date": "+33 2 00 00 00 00",
                "revenue": 0
            },
            {
                "date": "+33 2 00 00 00 00",
                "revenue": 257.18
            },
            {
                "date": "+33 2 00 00 00 00",
                "revenue": 385.24
            },
            {
                "date": "+33 2 00 00 00 00",
                "revenue": 303.63
            },
            "… 27 more, trimmed for the example"
        ],
        "top_products": [
            {
                "name": "Test Product (Vendor 2)",
                "quantity": 7
            },
            {
                "name": "Slow-Roasted Coffee Beans — 250 g",
                "quantity": 6
            },
            {
                "name": "Speckled Scented Candle — 380 g",
                "quantity": 4
            },
            {
                "name": "Anodised Keyboard — Tenkeyless",
                "quantity": 3
            },
            "… 1 more, trimmed for the example"
        ]
    },
    "meta": null,
    "error": null
}

The user groups

GET /groups read since 6.6.0

The CMS user groups, for the pickers that restrict a price, a discount or a product to some of them. The list is the same shape on Joomla and on WordPress, which is the point of it: a client does not need to know which it is talking to.

These are the ids an access object holds. They are user groups and not Joomla view levels.

Response a list

FieldTypeDescription
idintegerWhat an access object stores.
titlestringThe name of the group.
GET/groups
curl "$SHOP/hikashop-api/v1/groups" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 1,
            "title": "Mr"
        },
        {
            "id": 9,
            "title": "Mr"
        },
        {
            "id": 6,
            "title": "Mr"
        },
        {
            "id": 7,
            "title": "Mr"
        },
        "… 5 more, trimmed for the example"
    ],
    "meta": null,
    "error": null
}

Price a product in an order

GET /orders/{id}/products/precompute read since 6.6.0

What a product would cost on this order, before adding it. The order's customer, currency and zone all bear on the price, so this asks the shop rather than guessing from the catalogue.

Use it to show a line before it is committed, then POST /orders/{id}/products to add it.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Query

ParameterTypeDescription
product_idrequiredintegerThe product or variant to price.
quantityintegerHow many, which matters where the product has quantity breaks. Defaults to 1.

Response

FieldTypeDescription
product_idintegerWhat was priced.
namestring
codestringIts SKU.
quantityintegerThe quantity the price was worked out for.
pricenumberUnit price, tax excluded, in the order currency.
taxnumberTax per unit, worked out for this order rather than in general.
tax_namekeysstring[]Which tax rates that came from.

Errors

CodeHTTPMeans
not_found404No such order or no such product, or the operator may not see them.
GET/orders/{id}/products/precompute?product_id=1&quantity=2
curl "$SHOP/hikashop-api/v1/orders/{id}/products/precompute?product_id=1&quantity=2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "product_id": 1,
        "name": "Test Product (Vendor 2)",
        "code": "TEST001",
        "quantity": 2,
        "price": 9.99,
        "tax": 0,
        "tax_namekeys": []
    },
    "meta": null,
    "error": null
}

Read one customer

GET /customers/{id} read since 6.6.0

A customer with their addresses, their orders, which groups they are in and your own customer fields.

can_edit_account and groups_editable are worth reading before drawing a form: they say whether this operator may change this particular account at all, so a client can leave the controls out rather than offer something that will be refused. A customer can have many addresses and one default of each kind.

Path

ParameterTypeDescription
idrequiredintegerThe customer id.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer, or the operator may not see them.
GET/customers/{id}
curl "$SHOP/hikashop-api/v1/customers/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 24,
                "types": [
                    "billing"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 23,
                "types": [
                    "shipping"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            }
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Delete a customer

DELETE /customers/{id} write since 6.6.0

Removes a customer and their addresses.

A customer with orders is refused, which is what the backend does too: delete their orders first, and then the customer.

Path

ParameterTypeDescription
idrequiredintegerThe customer id.

Response

FieldTypeDescription
deletedbooleanTrue when the row is gone.

Errors

CodeHTTPMeans
not_found404No such customer, or the operator may not delete them.
has_orders400They have orders. Delete those first.
delete_failed400The shop refused to delete the row.
DELETE/customers/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "deleted": 5272
    },
    "meta": null,
    "error": null
}

Update a customer's profile

PUT /customers/{id} write since 6.6.0

Changes the name, the email address, the login, the password, the user groups and your own customer fields, and answers with the customer as it now stands.

The password is written straight into the CMS account and is never returned by anything. Changing the email of a registered customer changes the account they log in with, which is why a clash is refused rather than silently ignored.

Path

ParameterTypeDescription
idrequiredintegerThe customer id.

Body

FieldTypeDescription
namestringTheir display name.
emailstringMust not belong to another account.
usernamestringThe login, for a registered customer.
passwordstringA new password. Send it only when changing it.
groupsinteger[]The user groups they belong to, as ids from GET /groups.
custom_fieldsobjectYour own customer fields, keyed by namekey.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer, or the operator may not change them.
invalid_email400The email address is not one.
forbidden_target400The operator may not change this particular account, which is how a super user is protected from being edited by staff.
username_taken400Another account has that login.
email_taken400Another account has that email address.
account_save_failed400The CMS refused to save the account.
invalid_fields400One of your own fields was rejected by its own rules.
PUT/customers/{id}
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Alex Marchand"
}'
200success
{
    "data": {
        "id": 20,
        "cms_id": 0,
        "name": "John Doe",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "",
        "type": "guest",
        "blocked": false,
        "can_edit_account": true,
        "groups_editable": false,
        "groups": [],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1783721823,
        "addresses": [
            {
                "id": 26,
                "types": [
                    "billing"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 25,
                "types": [
                    "shipping"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            }
        ],
        "orders": [
            {
                "id": 26,
                "number": "B2D6",
                "status": "created",
                "created": 1785338765,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 28,
                "number": "B2F8",
                "status": "confirmed",
                "created": 1783722444,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 30,
                "number": "ZBUNDLE",
                "status": "confirmed",
                "created": 1783722444,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 22,
                "number": "Z22",
                "status": "created",
                "created": 1783721823,
                "total": 14.99,
                "currency_id": 1
            }
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Give a guest an account

POST /customers/{id}/account write since 6.6.0

Turns a guest into a registered customer, keeping their orders and addresses, and answers with the customer as it now stands.

This is the useful direction: someone ordered without an account, then asked for one. Creating the account separately would leave their history behind.

Path

ParameterTypeDescription
idrequiredintegerThe guest customer.

Body

FieldTypeDescription
usernamerequiredstringThe login to create.
passwordrequiredstringTheir password. Never returned by anything afterwards.
namestringTheir display name, defaulting to the one on the guest record.
groupsinteger[]The user groups to put them in.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer.
already_registered400They already have an account.
missing_credentials400A username and a password are both required.
invalid_email400The address on the guest record is not usable as an account email.
email_taken400Another account has that email address.
username_taken400Another account has that login.
account_save_failed400The CMS refused to create the account.
POST/customers/{id}/account
curl -X POST "$SHOP/hikashop-api/v1/customers/{id}/account" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "doccapture5271",
    "password": "not-a-real-password-9x"
}'
200success
{
    "data": {
        "id": 5271,
        "cms_id": 171,
        "name": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "doccapture5271",
        "type": "registered",
        "blocked": false,
        "can_edit_account": true,
        "groups_editable": false,
        "groups": [
            {
                "id": 2,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1786353021,
        "addresses": [
            {
                "id": 4362,
                "types": [
                    "billing"
                ],
                "name": "E2E Tester",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            }
        ],
        "orders": [],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Create a product

POST /products public since 6.6.0

Creates a product and answers with its id. Only the name is required; everything else can be set now or later with PUT /products/{id}.

Prices, images and categories are separate calls, so a new product is not sellable until it has at least one price.

Body

Any of the fields a product carries: name (required), code, description, published, msrp, gtin, condition, weight, weight_unit, width, height, length, dimension_unit, min_per_order, max_per_order, sale_start, sale_end, page_title, meta_description, keywords, canonical, url, alias, tax_id, manufacturer_id, contact, warehouse_id, plus access and custom_fields.

They mean what they mean on GET /products/{id}. A field the shop's own schema does not have is ignored rather than refused, which is what lets one client talk to shops of different vintages.

Response

FieldTypeDescription
idstring
namestring
codestringThe SKU. Unique within the shop.
descriptionstringThe long description, as HTML.
description_typestringWhich editor the description was written with.
publishedboolean
quantityinteger-1 when this product does not track stock, which is not the same as 0.
msrpintegerThe manufacturer's suggested price, shown struck through when the shop is configured to.
gtinstringThe barcode: EAN, UPC or ISBN. This is what GET /products/lookup matches on.
conditionstringNew, used, refurbished. Used by the feeds rather than by the shop itself.
weightintegerShipping weight, in weight_unit.
weight_unitstringkg, g, lb or oz.
widthintegerIn dimension_unit.
heightintegerIn dimension_unit.
lengthintegerIn dimension_unit.
dimension_unitstringm, cm, mm, ft or in.
min_per_orderintegerThe smallest quantity a customer may order, 0 for no minimum.
max_per_orderintegerThe largest, 0 for no maximum.
sale_startinteger|nullUnix timestamp before which the product is not on sale.
sale_endinteger|nullUnix timestamp after which it is no longer sold.
page_titlestringSEO title, empty to use the name.
meta_descriptionstringSEO description.
keywordsstringSEO keywords.
canonicalstringA canonical URL, when this page should point at another.
urlstringThe address of the product page on the shop.
aliasstringThe slug used in that address.
accessobjectWho may see the product: mode (all, none or groups) and groups, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User group ids, meaningful only when the mode is groups.
contactbooleanWhether this product is enquired about rather than bought.
warehouse_idintegerThe warehouse holding the stock, 0 when the shop has none.
typestringmain for a product, variant for one of its variants.
parent_idintegerThe parent product when this is a variant, 0 otherwise.
value_idsinteger[]For a variant, the characteristic values it stands for.
manufacturer_idintegerThe brand, 0 when unset.
manufacturer_namestringIts name, saving a second call.
tax_idintegerThe tax category, 0 when the product is untaxed.
tax_namestringIts name.
tax_ratenumberThe rate as a fraction, so 0.2 is twenty percent.
pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
idstring
valuestringTax excluded, as stored.
currency_idstring
min_quantitystringFrom how many items this row applies, which is how quantity breaks are expressed.
accessobjectWho this price is for, in the same shape as the product access.
modestringAs above.
groupsinteger[]As above.
usersinteger[]Named customers, empty for everyone.
zone_idsinteger[]Zones this price applies in, empty for everywhere.
start_dateinteger|nullUnix timestamp.
end_dateinteger|nullUnix timestamp.
imagesobject[]In the order the editor shows them; the first is the main image.
idstring
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingstring
descriptionstringThe alt text.
accessobjectWho may see it.
modestringAs above.
groupsinteger[]As above.
free_downloadstringFiles only; meaningless on an image.
filesobject[]Downloadable files, in the same shape as the images.
idstring
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderingstring
descriptionstring
accessobjectWho may download it.
free_downloadstringWhether it can be downloaded without buying the product.
categoriesobject[]The categories the product is in.
idinteger
namestring
bundleobject[]The products this one is made of, when it is a bundle.
idstring
namestring
codestring
quantitystringHow many of it the bundle contains.
optionsobject[]Products offered as options alongside this one.
idstring
namestring
codestring
quantitystring
relatedobject[]Products shown as related.
idstring
namestring
codestring
quantitystring
tagsinteger[]CMS tag ids.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
idintegerThe characteristic, such as Size.
namestringIts name.
valuesobject[]The values of it this product uses, such as S, M and L.
idintegerThe value id, which is what a variant refers to.
valuestringIts name, such as M.
variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
idintegerThe variant is a product in its own right, and this is its id.
codestringIts own SKU.
quantityintegerIts own stock. This is the figure to change, not the parent's.
publishedboolean
pricenumber|nullnull when the variant has no price of its own and the parent's applies.
valuesobject[]Which characteristic values this variant stands for, one per characteristic.
option_idintegerThe characteristic.
option_namestringIts name, so the variant can be labelled without a second call.
value_idintegerThe value.
valuestringIts name.
imagesobject[]The variant's own images, in the same shape as the product's.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderinginteger
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
namekeystringThe key used in custom_fields.
typestringtext, radio, singledropdown, file, and the rest of HikaShop's field types.
raw_typestringHikaShop's own name for the type, before it is mapped to something a client can render.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save the product without it.
optionsobject[]The choices, for a field that has them. Empty for a free text one.
multiplebooleanWhether more than one choice may be selected.
translatablebooleanWhether its value can be translated, which is what the translation routes offer.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts, comma separated. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor custom fields holding a file, the file behind each value. Keyed the same way.

Errors

CodeHTTPMeans
invalid_fields400One of your own fields was rejected by its own rules.
save_failed500The shop refused to save the product.
POST/products
curl -X POST "$SHOP/hikashop-api/v1/products" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Documentation capture product",
    "code": "DOC-CAPTURE-1"
}'
200success
{
    "data": {
        "id": 9131,
        "name": "Documentation capture product",
        "code": "DOC-CAPTURE-1",
        "description": "",
        "description_type": "",
        "published": false,
        "quantity": -1,
        "msrp": 0,
        "gtin": "",
        "condition": "",
        "weight": 0,
        "weight_unit": "kg",
        "width": 0,
        "height": 0,
        "length": 0,
        "dimension_unit": "m",
        "min_per_order": 0,
        "max_per_order": 0,
        "sale_start": 0,
        "sale_end": 0,
        "page_title": "",
        "meta_description": "",
        "keywords": "",
        "canonical": "",
        "url": "",
        "alias": "documentation-capture-product",
        "access": {
            "mode": "all",
            "groups": []
        },
        "contact": false,
        "warehouse_id": 0,
        "type": "main",
        "parent_id": 0,
        "value_ids": [],
        "manufacturer_id": 0,
        "manufacturer_name": "",
        "tax_id": 0,
        "tax_name": "",
        "tax_rate": 0,
        "prices": [],
        "images": [],
        "files": [],
        "categories": [
            {
                "id": 2,
                "name": "product category"
            }
        ],
        "bundle": [],
        "options": [],
        "related": [],
        "tags": [],
        "characteristics": [],
        "variants": [],
        "fields": [
            {
                "namekey": "test_ajax_image",
                "type": "ajaximage",
                "raw_type": "ajaximage",
                "label": "Test Ajax Image",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "product_gtin",
                "type": "text",
                "raw_type": "text",
                "label": "GTIN",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            }
        ],
        "custom_fields": {
            "test_ajax_image": null,
            "product_gtin": null
        },
        "custom_field_files": {
            "test_ajax_image": []
        }
    },
    "meta": null,
    "error": null
}

Update a product

PUT /products/{id} write since 6.6.0

Changes the fields you send and leaves the rest alone, which is the opposite of the price and category calls: this one merges.

Sending nothing to change is refused rather than treated as a success, so a client cannot believe it saved something it did not.

Path

ParameterTypeDescription
idrequiredintegerThe product or variant.

Body

The same fields as POST /products, all of them optional here. A field you do not send keeps its value; access and custom_fields are accepted too.

Response

FieldTypeDescription
idstring
namestring
codestringThe SKU. Unique within the shop.
descriptionstringThe long description, as HTML.
description_typestringWhich editor the description was written with.
publishedboolean
quantityinteger-1 when this product does not track stock, which is not the same as 0.
msrpintegerThe manufacturer's suggested price, shown struck through when the shop is configured to.
gtinstringThe barcode: EAN, UPC or ISBN. This is what GET /products/lookup matches on.
conditionstringNew, used, refurbished. Used by the feeds rather than by the shop itself.
weightnumberShipping weight, in weight_unit.
weight_unitstringkg, g, lb or oz.
widthintegerIn dimension_unit.
heightintegerIn dimension_unit.
lengthintegerIn dimension_unit.
dimension_unitstringm, cm, mm, ft or in.
min_per_orderintegerThe smallest quantity a customer may order, 0 for no minimum.
max_per_orderintegerThe largest, 0 for no maximum.
sale_startinteger|nullUnix timestamp before which the product is not on sale.
sale_endinteger|nullUnix timestamp after which it is no longer sold.
page_titlestringSEO title, empty to use the name.
meta_descriptionstringSEO description.
keywordsstringSEO keywords.
canonicalstringA canonical URL, when this page should point at another.
urlstringThe address of the product page on the shop.
aliasstringThe slug used in that address.
accessobjectWho may see the product: mode (all, none or groups) and groups, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User group ids, meaningful only when the mode is groups.
contactbooleanWhether this product is enquired about rather than bought.
warehouse_idintegerThe warehouse holding the stock, 0 when the shop has none.
typestringmain for a product, variant for one of its variants.
parent_idintegerThe parent product when this is a variant, 0 otherwise.
value_idsinteger[]For a variant, the characteristic values it stands for.
manufacturer_idintegerThe brand, 0 when unset.
manufacturer_namestringIts name, saving a second call.
tax_idintegerThe tax category, 0 when the product is untaxed.
tax_namestringIts name.
tax_ratenumberThe rate as a fraction, so 0.2 is twenty percent.
pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
idinteger
valuenumberTax excluded, as stored.
currency_idinteger
min_quantityintegerFrom how many items this row applies, which is how quantity breaks are expressed.
accessobjectWho this price is for, in the same shape as the product access.
modestringAs above.
groupsinteger[]As above.
usersinteger[]Named customers, empty for everyone.
zone_idsinteger[]Zones this price applies in, empty for everywhere.
start_dateinteger|nullUnix timestamp.
end_dateinteger|nullUnix timestamp.
imagesobject[]In the order the editor shows them; the first is the main image.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderinginteger
descriptionstringThe alt text.
accessobjectWho may see it.
modestringAs above.
groupsinteger[]As above.
free_downloadbooleanFiles only; meaningless on an image.
filesobject[]Downloadable files, in the same shape as the images.
idstring
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderingstring
descriptionstring
accessobjectWho may download it.
free_downloadstringWhether it can be downloaded without buying the product.
categoriesobject[]The categories the product is in.
idinteger
namestring
bundleobject[]The products this one is made of, when it is a bundle.
idstring
namestring
codestring
quantitystringHow many of it the bundle contains.
optionsobject[]Products offered as options alongside this one.
idstring
namestring
codestring
quantitystring
relatedobject[]Products shown as related.
idstring
namestring
codestring
quantitystring
tagsinteger[]CMS tag ids.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
idintegerThe characteristic, such as Size.
namestringIts name.
valuesobject[]The values of it this product uses, such as S, M and L.
idintegerThe value id, which is what a variant refers to.
valuestringIts name, such as M.
variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
idintegerThe variant is a product in its own right, and this is its id.
codestringIts own SKU.
quantityintegerIts own stock. This is the figure to change, not the parent's.
publishedboolean
pricenumber|nullnull when the variant has no price of its own and the parent's applies.
valuesobject[]Which characteristic values this variant stands for, one per characteristic.
option_idintegerThe characteristic.
option_namestringIts name, so the variant can be labelled without a second call.
value_idintegerThe value.
valuestringIts name.
imagesobject[]The variant's own images, in the same shape as the product's.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderinginteger
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
namekeystringThe key used in custom_fields.
typestringtext, radio, singledropdown, file, and the rest of HikaShop's field types.
raw_typestringHikaShop's own name for the type, before it is mapped to something a client can render.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save the product without it.
optionsobject[]The choices, for a field that has them. Empty for a free text one.
multiplebooleanWhether more than one choice may be selected.
translatablebooleanWhether its value can be translated, which is what the translation routes offer.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts, comma separated. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor custom fields holding a file, the file behind each value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
invalid_fields400One of your own fields was rejected by its own rules.
nothing400The body held no field this shop knows, so nothing would have been written.
PUT/products/{id}
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Desk lamp, brass"
}'
200success
{
    "data": {
        "id": 9028,
        "name": "Desk lamp, brass",
        "code": "DEMO-0300",
        "description": "Chosen because it does one thing properly and nothing else at all.",
        "description_type": "",
        "published": true,
        "quantity": 52,
        "msrp": 0,
        "gtin": "",
        "condition": "",
        "weight": 1.083,
        "weight_unit": "kg",
        "width": 0,
        "height": 0,
        "length": 0,
        "dimension_unit": "m",
        "min_per_order": 0,
        "max_per_order": 0,
        "sale_start": 0,
        "sale_end": 0,
        "page_title": "",
        "meta_description": "",
        "keywords": "",
        "canonical": "",
        "url": "",
        "alias": "desk-lamp-brass",
        "access": {
            "mode": "all",
            "groups": []
        },
        "contact": false,
        "warehouse_id": 0,
        "type": "main",
        "parent_id": 0,
        "value_ids": [],
        "manufacturer_id": 0,
        "manufacturer_name": "",
        "tax_id": 0,
        "tax_name": "",
        "tax_rate": 0,
        "prices": [
            {
                "id": 4930,
                "value": 19.9,
                "currency_id": 1,
                "min_quantity": 1,
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "users": [],
                "zone_ids": [],
                "start_date": 0,
                "end_date": 0
            }
        ],
        "images": [
            {
                "id": 8298,
                "name": "Recycled Fountain Pen — Broad",
                "path": "demo-0300.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0300.png",
                "ordering": 1,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            }
        ],
        "files": [],
        "categories": [
            {
                "id": 230,
                "name": "Planners"
            }
        ],
        "bundle": [],
        "options": [],
        "related": [],
        "tags": [],
        "characteristics": [],
        "variants": [],
        "fields": [
            {
                "namekey": "test_ajax_image",
                "type": "ajaximage",
                "raw_type": "ajaximage",
                "label": "Test Ajax Image",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "product_gtin",
                "type": "text",
                "raw_type": "text",
                "label": "GTIN",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            }
        ],
        "custom_fields": {
            "test_ajax_image": null,
            "product_gtin": null
        },
        "custom_field_files": {
            "test_ajax_image": []
        }
    },
    "meta": null,
    "error": null
}

Delete a product

DELETE /products/{id} write since 6.6.0

Deletes a product and its variants.

Orders that already contain it are untouched: an order line records what was sold at the time, and it does not stop meaning something because the catalogue changed.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Response

FieldTypeDescription
idintegerThe product that was deleted.
deletedbooleanTrue when the row is gone.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not delete it.
delete_failed400The shop refused to delete it.
DELETE/products/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/products/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 9028,
        "deleted": true
    },
    "meta": null,
    "error": null
}

Reconcile the variant set

PUT /products/{id}/variants write since 6.6.0

Takes the variants a product should have and makes the shop agree: it creates the ones that are missing, updates the ones that exist and removes the ones you left out.

A variant is identified by the characteristic values it stands for rather than by an id, because that is what makes it that variant. Send the whole set.

Path

ParameterTypeDescription
idrequiredintegerThe parent product.

Body

FieldTypeDescription
variantsrequiredobject[]The complete set. Each needs the characteristic values it stands for, and may carry code, quantity, published and price.

Response

FieldTypeDescription
characteristicsobject[]The characteristics the product now varies on, as they stand after the change.
variantsobject[]The variants as they now stand, in the same shape as on the product.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
PUT/products/1/variants
curl -X PUT "$SHOP/hikashop-api/v1/products/1/variants" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "variants": []
}'
200success
{
    "data": {
        "characteristics": [],
        "variants": []
    },
    "meta": null,
    "error": null
}

Edit one variant

PUT /products/{id}/variants/{vid} write since 6.6.0

Changes one variant without touching the others, which is what a stock correction or a price change on a single size needs.

Path

ParameterTypeDescription
idrequiredintegerThe parent product.
vidrequiredintegerThe variant.

Body

Any of code, quantity, published and price, and your own product fields. What you do not send is left alone.

Response

FieldTypeDescription
idstring
namestring
codestringThe SKU. Unique within the shop.
descriptionstringThe long description, as HTML.
description_typestringWhich editor the description was written with.
publishedboolean
quantityinteger-1 when this product does not track stock, which is not the same as 0.
msrpintegerThe manufacturer's suggested price, shown struck through when the shop is configured to.
gtinstringThe barcode: EAN, UPC or ISBN. This is what GET /products/lookup matches on.
conditionstringNew, used, refurbished. Used by the feeds rather than by the shop itself.
weightintegerShipping weight, in weight_unit.
weight_unitstringkg, g, lb or oz.
widthintegerIn dimension_unit.
heightintegerIn dimension_unit.
lengthintegerIn dimension_unit.
dimension_unitstringm, cm, mm, ft or in.
min_per_orderintegerThe smallest quantity a customer may order, 0 for no minimum.
max_per_orderintegerThe largest, 0 for no maximum.
sale_startinteger|nullUnix timestamp before which the product is not on sale.
sale_endinteger|nullUnix timestamp after which it is no longer sold.
page_titlestringSEO title, empty to use the name.
meta_descriptionstringSEO description.
keywordsstringSEO keywords.
canonicalstringA canonical URL, when this page should point at another.
urlstringThe address of the product page on the shop.
aliasstringThe slug used in that address.
accessobjectWho may see the product: mode (all, none or groups) and groups, which are user **group** ids and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User group ids, meaningful only when the mode is groups.
contactbooleanWhether this product is enquired about rather than bought.
warehouse_idintegerThe warehouse holding the stock, 0 when the shop has none.
typestringmain for a product, variant for one of its variants.
parent_idintegerThe parent product when this is a variant, 0 otherwise.
value_idsinteger[]For a variant, the characteristic values it stands for.
manufacturer_idintegerThe brand, 0 when unset.
manufacturer_namestringIts name, saving a second call.
tax_idintegerThe tax category, 0 when the product is untaxed.
tax_namestringIts name.
tax_ratenumberThe rate as a fraction, so 0.2 is twenty percent.
pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
idstring
valuestringTax excluded, as stored.
currency_idstring
min_quantitystringFrom how many items this row applies, which is how quantity breaks are expressed.
accessobjectWho this price is for, in the same shape as the product access.
modestringAs above.
groupsinteger[]As above.
usersinteger[]Named customers, empty for everyone.
zone_idsinteger[]Zones this price applies in, empty for everywhere.
start_dateinteger|nullUnix timestamp.
end_dateinteger|nullUnix timestamp.
imagesobject[]In the order the editor shows them; the first is the main image.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderinginteger
descriptionstringThe alt text.
accessobjectWho may see it.
modestringAs above.
groupsinteger[]As above.
free_downloadbooleanFiles only; meaningless on an image.
filesobject[]Downloadable files, in the same shape as the images.
idstring
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderingstring
descriptionstring
accessobjectWho may download it.
free_downloadstringWhether it can be downloaded without buying the product.
categoriesobject[]The categories the product is in.
idstring
namestring
bundleobject[]The products this one is made of, when it is a bundle.
idstring
namestring
codestring
quantitystringHow many of it the bundle contains.
optionsobject[]Products offered as options alongside this one.
idstring
namestring
codestring
quantitystring
relatedobject[]Products shown as related.
idstring
namestring
codestring
quantitystring
tagsinteger[]CMS tag ids.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
idintegerThe characteristic, such as Size.
namestringIts name.
valuesobject[]The values of it this product uses, such as S, M and L.
idintegerThe value id, which is what a variant refers to.
valuestringIts name, such as M.
variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
idintegerThe variant is a product in its own right, and this is its id.
codestringIts own SKU.
quantityintegerIts own stock. This is the figure to change, not the parent's.
publishedboolean
pricenumber|nullnull when the variant has no price of its own and the parent's applies.
valuesobject[]Which characteristic values this variant stands for, one per characteristic.
option_idintegerThe characteristic.
option_namestringIts name, so the variant can be labelled without a second call.
value_idintegerThe value.
valuestringIts name.
imagesobject[]The variant's own images, in the same shape as the product's.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute.
orderinginteger
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
namekeystringThe key used in custom_fields.
typestringtext, radio, singledropdown, file, and the rest of HikaShop's field types.
raw_typestringHikaShop's own name for the type, before it is mapped to something a client can render.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save the product without it.
optionsobject[]The choices, for a field that has them. Empty for a free text one.
multiplebooleanWhether more than one choice may be selected.
translatablebooleanWhether its value can be translated, which is what the translation routes offer.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts, comma separated. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor custom fields holding a file, the file behind each value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such variant, or the operator may not change it.
invalid_fields400One of your own fields was rejected by its own rules.
PUT/products/{id}/variants/{id}
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/variants/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 7
}'
200success
{
    "data": {
        "id": 8646,
        "name": "Merino Socks — Pair — XS",
        "code": "DEMO-0012-XS",
        "description": "",
        "description_type": "",
        "published": true,
        "quantity": 7,
        "msrp": 0,
        "gtin": "",
        "condition": "",
        "weight": 0,
        "weight_unit": "kg",
        "width": 0,
        "height": 0,
        "length": 0,
        "dimension_unit": "m",
        "min_per_order": 0,
        "max_per_order": 0,
        "sale_start": 0,
        "sale_end": 0,
        "page_title": "",
        "meta_description": "",
        "keywords": "",
        "canonical": "",
        "url": "",
        "alias": "",
        "access": {
            "mode": "all",
            "groups": []
        },
        "contact": false,
        "warehouse_id": 0,
        "type": "variant",
        "parent_id": 8645,
        "value_ids": [
            35
        ],
        "manufacturer_id": 0,
        "manufacturer_name": "",
        "tax_id": 0,
        "tax_name": "",
        "tax_rate": 0,
        "prices": [],
        "images": [
            {
                "id": 7770,
                "name": "Merino Socks — Pair — XS",
                "path": "demo-0012-xs.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0012-xs.png",
                "ordering": 1,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            }
        ],
        "files": [],
        "categories": [],
        "bundle": [],
        "options": [],
        "related": [],
        "tags": [],
        "characteristics": [],
        "variants": [],
        "fields": [
            {
                "namekey": "test_ajax_image",
                "type": "ajaximage",
                "raw_type": "ajaximage",
                "label": "Test Ajax Image",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "product_gtin",
                "type": "text",
                "raw_type": "text",
                "label": "GTIN",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            }
        ],
        "custom_fields": {
            "test_ajax_image": null,
            "product_gtin": null
        },
        "custom_field_files": {
            "test_ajax_image": []
        }
    },
    "meta": null,
    "error": null
}

Apply a coupon to an order

POST /orders/{id}/coupon write since 6.6.0

Applies a coupon by its code and re-totals the order.

The shop validates it again here against this order: its dates, its quota, its minimum, the products in the cart and the customer. A coupon that appears in GET /coupons can still be refused for this particular order, which is the point of validating rather than trusting the list.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Body

FieldTypeDescription
coderequiredstringThe coupon code, as the customer would type it.

Response

FieldTypeDescription
idintegerThe order.
feesobjectThe discount, shipping and payment amounts of the order.
discountobjectIts amount, its tax, the tax_namekeys behind that tax, and the coupon code when one was used.
amountnumberA positive figure, already subtracted from the total.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
codestringThe coupon code, empty for a discount applied by hand.
shippingobjectThe shipping charge and what carried it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that handled it.
method_namestringAs the merchant named it.
paymentobjectThe payment fee and what took it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that took it.
method_namestringAs the merchant named it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not change it.
invalid_coupon400No such code, or it does not apply to this order.
save_failed500The order could not be saved.
POST/orders/{id}/coupon
curl -X POST "$SHOP/hikashop-api/v1/orders/{id}/coupon" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "APPTEST10"
}'
200success
{
    "data": {
        "id": 5630,
        "fees": {
            "discount": {
                "amount": 10,
                "tax": 0,
                "tax_namekeys": [],
                "code": "APPTEST10"
            },
            "shipping": {
                "amount": 6.9,
                "tax": 0,
                "tax_namekeys": [],
                "method": "manual",
                "method_name": "Vendor 2 only shipping"
            },
            "payment": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "method": "paypalcheckout",
                "method_name": "PayPal Checkout Express Test"
            }
        },
        "totals": {
            "total": 303.92,
            "discount": 10,
            "shipping": 6.9,
            "payment": 0,
            "tax": 0
        }
    },
    "meta": null,
    "error": null
}

Remove the discount from an order

DELETE /orders/{id}/coupon write since 6.6.0

Takes the coupon or the hand-applied discount off the order and re-totals it. There is nothing to send.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Response

FieldTypeDescription
idintegerThe order.
feesobjectThe discount, shipping and payment amounts of the order.
discountobjectIts amount, its tax, the tax_namekeys behind that tax, and the coupon code when one was used.
amountnumberA positive figure, already subtracted from the total.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
codestringThe coupon code, empty for a discount applied by hand.
shippingobjectThe shipping charge and what carried it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that handled it.
method_namestringAs the merchant named it.
paymentobjectThe payment fee and what took it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that took it.
method_namestringAs the merchant named it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not change it.
save_failed500The order could not be saved.
DELETE/orders/{id}/coupon
curl -X DELETE "$SHOP/hikashop-api/v1/orders/{id}/coupon" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 5630,
        "fees": {
            "discount": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "code": ""
            },
            "shipping": {
                "amount": 6.9,
                "tax": 0,
                "tax_namekeys": [],
                "method": "manual",
                "method_name": "Vendor 2 only shipping"
            },
            "payment": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "method": "paypalcheckout",
                "method_name": "PayPal Checkout Express Test"
            }
        },
        "totals": {
            "total": 313.92,
            "discount": 0,
            "shipping": 6.9,
            "payment": 0,
            "tax": 0
        }
    },
    "meta": null,
    "error": null
}

Add a line to an order

POST /orders/{id}/products write since 6.6.0

Adds a product to an existing order and re-totals it.

Send a price only to override what the shop would charge; leave it out and the order's own pricing applies, which is what GET /orders/{id}/products/precompute shows you beforehand.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Body

FieldTypeDescription
product_idrequiredintegerThe product or variant to add.
quantityintegerHow many. Defaults to 1.
pricenumberUnit price, tax excluded, to override the shop. Omit to let the shop price it.
tax_namekeysstring[]Which tax rates to apply, when overriding the price. Omit to let the shop decide.

Response

FieldTypeDescription
idintegerThe order.
itemsobject[]The lines as they now stand, in the same shape as on the order.
idintegerThe line id, which is what the line routes take. It is not the product id.
namestringThe product as it was named when ordered.
codestringIts SKU at the time.
quantityinteger
pricenumberUnit price, tax excluded, as agreed at the time.
taxnumberTax on the line.
editablebooleanFalse once the line can no longer be changed.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.

Errors

CodeHTTPMeans
not_found404No such order or product, or the operator may not change the order.
save_failed500The order could not be saved.
POST/orders/{id}/products
curl -X POST "$SHOP/hikashop-api/v1/orders/{id}/products" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "product_id": "1",
    "quantity": 1
}'
200success
{
    "data": {
        "id": 5630,
        "items": [
            {
                "id": 20630,
                "name": "Nordic Design item",
                "code": "SEED-7-44",
                "quantity": 1,
                "price": 307.02,
                "tax": 0,
                "editable": true
            },
            {
                "id": 20632,
                "name": "Test Product (Vendor 2)",
                "code": "TEST001",
                "quantity": 1,
                "price": 0,
                "tax": 0,
                "editable": true
            }
        ],
        "totals": {
            "total": 313.92,
            "discount": 0,
            "shipping": 6.9,
            "payment": 0,
            "tax": 0
        }
    },
    "meta": null,
    "error": null
}

Change a line's quantity

PUT /orders/{id}/products/{lineId} write since 6.6.0

Changes how many of one line the order holds, and re-totals it. A quantity of 0 removes the line.

The id in the path is the line id from items, not the product id: the same product can appear on an order more than once.

Path

ParameterTypeDescription
idrequiredintegerThe order.
lineIdrequiredintegerThe line, from `items[].id` on the order.

Body

FieldTypeDescription
quantityrequiredintegerThe new quantity. 0 removes the line.

Response

FieldTypeDescription
idintegerThe order.
itemsobject[]The lines as they now stand.
idintegerThe line id, which is what the line routes take. It is not the product id.
namestringThe product as it was named when ordered.
codestringIts SKU at the time.
quantityinteger
pricenumberUnit price, tax excluded, as agreed at the time.
taxnumberTax on the line.
editablebooleanFalse once the line can no longer be changed.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.

Errors

CodeHTTPMeans
not_found404No such order or line, or the operator may not change it.
save_failed500The order could not be saved.
PUT/orders/{id}/products/{id}
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/products/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 2
}'
200success
{
    "data": {
        "id": 5630,
        "items": [
            {
                "id": 20630,
                "name": "Nordic Design item",
                "code": "SEED-7-44",
                "quantity": 1,
                "price": 307.02,
                "tax": 0,
                "editable": true
            },
            {
                "id": 20632,
                "name": "Test Product (Vendor 2)",
                "code": "TEST001",
                "quantity": 2,
                "price": 0,
                "tax": 0,
                "editable": true
            }
        ],
        "totals": {
            "total": 313.92,
            "discount": 0,
            "shipping": 6.9,
            "payment": 0,
            "tax": 0
        }
    },
    "meta": null,
    "error": null
}

Run a mass action

POST /massactions/{id} write since 6.6.0

Runs one of the merchant's own bulk operations over a selection.

It runs the same code the backend runs, so whatever the action does there it does here, including whatever a third-party plugin added to it. Give it the ids to work on.

Path

ParameterTypeDescription
idrequiredintegerThe mass action, from `GET /massactions`.

Body

FieldTypeDescription
idsrequiredinteger[]The records to run it over, from the listing the action belongs to.

Response

FieldTypeDescription
okbooleanWhether the action reported success.
countintegerHow many records it worked on.
reportstringWhatever the action had to say, ready to show. Its wording is the action's own.

Errors

CodeHTTPMeans
invalid_request400No ids were sent, or the action is not one this shop has.
forbidden403The operator may not work on that kind of record.
POST/massactions/2
curl -X POST "$SHOP/hikashop-api/v1/massactions/2" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
        "9027"
    ]
}'
200success
{
    "data": {
        "ok": true,
        "count": 1,
        "report": []
    },
    "meta": null,
    "error": null
}

Create an order by hand

POST /orders write since 6.6.0

Creates an empty order for a customer, to be filled in with lines, fees and an address.

Give it either an existing user_id or a guest with at least an email address, which is how an order gets taken over the telephone from somebody who has never bought before.

Body

FieldTypeDescription
user_idintegerAn existing customer. Give this or guest.
guestobjectA new guest customer, needing at least email and usually a name.

Response

FieldTypeDescription
idintegerThe order that was created, to add lines to.

Errors

CodeHTTPMeans
no_customer400Neither a user_id nor a usable guest was given.
save_failed500The order could not be created.
POST/orders
curl -X POST "$SHOP/hikashop-api/v1/orders" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "guest": {
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "name": "Alex Marchand"
    }
}'
200success
{
    "data": {
        "id": 5632
    },
    "meta": null,
    "error": null
}

Save an order's custom fields

PUT /orders/{id}/fields write since 6.6.0

Writes the merchant's own order fields. Only the fields you send are touched.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by field namekey.

Response

FieldTypeDescription
idintegerThe order.
custom_fieldsobjectThe values as they now stand. The keys depend on the shop.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not change it.
invalid_fields400A field was rejected by its own rules.
save_failed500The order could not be saved.
PUT/orders/{id}/fields
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/fields" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": []
}'
200success
{
    "data": {
        "id": 5630,
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Save an address of an order

PUT /orders/{id}/address/{type} write since 6.6.0

Writes the billing or shipping address of an order, using the fields GET gave you.

The address on an order is its own copy, so changing it here does not change the customer's address book, and vice versa. That is deliberate: an invoice should not change because somebody moved.

Path

ParameterTypeDescription
idrequiredintegerThe order.
typerequiredstring`billing` or `shipping`.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by address field namekey, the same keys GET returned in values.

Response

FieldTypeDescription
typestringWhich address was written.
address_idintegerThe address row, created if there was none.
valuesobjectThe values as stored. Keyed by whatever address fields this shop has.
country_namestringThe country spelled out.
state_namestringThe state spelled out, empty where the country has none.
summaryobjectThe address ready to show, without re-reading the order.
namestring
companystring
formattedobjectLaid out the way this shop lays addresses out, which follows its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
streetstring
citystring
post_codestring

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not change it.
invalid_fields400A field was rejected by its own rules.
invalid_address400The address is not one the shop will accept, usually a missing required field.
PUT/orders/{id}/address/billing
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/address/billing" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
        "address_title": "Mr",
        "address_firstname": "Alex",
        "address_lastname": "Marchand",
        "address_street": "12 rue des Lilas",
        "address_post_code": "44000",
        "address_city": "Nantes",
        "address_telephone": "+33 2 00 00 00 00",
        "address_country": "country_France_73",
        "address_state": "state_Paris_1381"
    }
}'
200success
{
    "data": {
        "type": "billing",
        "address_id": 4366,
        "values": {
            "address_title": "Mr",
            "address_firstname": "Alex",
            "address_lastname": "Marchand",
            "address_company": "Lilas SARL",
            "address_street": "12 rue des Lilas",
            "address_post_code": "44000",
            "address_city": "Nantes",
            "address_telephone": "+33 2 00 00 00 00",
            "address_country": "country_France_73",
            "address_state": "state_Paris_1381",
            "address_vat": "FR00000000000"
        },
        "country_name": "France",
        "state_name": "Paris",
        "summary": {
            "name": "John Doe",
            "company": "Lilas SARL",
            "formatted": {
                "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
            },
            "street": "12 rue des Lilas",
            "city": "Nantes",
            "post_code": "44000"
        }
    },
    "meta": null,
    "error": null
}

Attach an image or a file

POST /products/{id}/images write since 6.6.0

Adds an image or a downloadable file to a product, either by sending the bytes or by pointing at something already in the upload folder.

Send data as base64 to upload, or path to attach a file that GET /media/browse already showed you. The extension is checked against what the shop allows, so a refusal is the shop's policy rather than a fault.

The same route serves /images and /files; which one you call decides where it goes.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Body

FieldTypeDescription
datastringThe bytes, base64 encoded. Give this or path.
pathstringA file already in the upload folder, relative to it, as GET /media/browse returns.
namestringThe file name to store it under. Defaults to the one in the path.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it, in the usual mode and groups shape.

Response

FieldTypeDescription
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingintegerWhere it sits among the others; the first image is the one the shop shows.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
free_downloadbooleanFiles only: whether it can be downloaded without buying the product.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
bad_type400The extension is not one this shop accepts.
write_failed500The upload folder refused the file, which is usually a permissions problem.
POST/products/{id}/images
curl -X POST "$SHOP/hikashop-api/v1/products/{id}/images" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFAAH/q842iQAAAABJRU5ErkJggg==",
    "name": "documentation-capture.png",
    "description": "A one pixel image"
}'
200success
{
    "data": {
        "id": 8325,
        "name": "documentation-capture.png",
        "path": "documentation-capture.png",
        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/documentation-capture.png",
        "ordering": 4,
        "description": "A one pixel image",
        "access": {
            "mode": "all",
            "groups": []
        },
        "free_download": false
    },
    "meta": null,
    "error": null
}

Edit an image or a file

PUT /products/{id}/files/{fileId} write since 6.6.0

Changes the name, the description or the access of something already attached, without re-uploading the bytes.

Path

ParameterTypeDescription
idrequiredintegerThe product.
fileIdrequiredintegerThe image or file.

Body

Any of name, description, access and, for a file, free_download. What you do not send is left alone.

Response

FieldTypeDescription
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingintegerWhere it sits among the others; the first image is the one the shop shows.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
free_downloadbooleanFiles only: whether it can be downloaded without buying the product.

Errors

CodeHTTPMeans
not_found404No such file on that product, or the operator may not change it.
PUT/products/{id}/files/{id}
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/files/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "documentation-capture.png",
    "description": "A one pixel image"
}'
200success
{
    "data": {
        "id": 8325,
        "name": "documentation-capture.png",
        "path": "documentation-capture.png",
        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/documentation-capture.png",
        "ordering": 4,
        "description": "A one pixel image",
        "access": {
            "mode": "all",
            "groups": []
        },
        "free_download": false
    },
    "meta": null,
    "error": null
}

Remove an image or a file

DELETE /products/{id}/files/{fileId} write since 6.6.0

Detaches an image or a file from a product.

The bytes stay in the upload folder: another product may be using the same file, and the API will not delete somebody's media because one product stopped pointing at it.

Path

ParameterTypeDescription
idrequiredintegerThe product.
fileIdrequiredintegerThe image or file.

Response

FieldTypeDescription
idintegerWhat was detached.
deletedbooleanTrue when the row is gone.

Errors

CodeHTTPMeans
not_found404No such file on that product, or the operator may not change it.
DELETE/products/{id}/files/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/products/{id}/files/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 8326,
        "deleted": true
    },
    "meta": null,
    "error": null
}

Reorder images and files

PUT /products/{id}/media/order write since 6.6.0

Sets the order of a product's images or files. The first image is the one the shop shows, so this is how you choose it.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Body

FieldTypeDescription
imagesinteger[]File ids in the order you want them.
filesinteger[]The same for downloadable files.

Response

FieldTypeDescription
imagesobject[]The images as they now stand, in their new order, each in the same shape as on the product.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingintegerWhere it sits among the others; the first image is the one the shop shows.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
free_downloadbooleanFiles only: whether it can be downloaded without buying the product.
filesobject[]The files as they now stand, in the same shape as on the product.
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingintegerWhere it sits among the others; the first image is the one the shop shows.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
free_downloadbooleanFiles only: whether it can be downloaded without buying the product.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
PUT/products/{id}/media/order
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/media/order" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "images": [
        7756,
        7757
    ]
}'
200success
{
    "data": {
        "images": [
            {
                "id": 7756,
                "name": "Lacquered Fountain Pen — Broad",
                "path": "demo-0002.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0002.png",
                "ordering": 0,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            },
            {
                "id": 7757,
                "name": "Lacquered Fountain Pen — Broad",
                "path": "demo-0002-2.png",
                "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0002-2.png",
                "ordering": 1,
                "description": "",
                "access": {
                    "mode": "all",
                    "groups": []
                },
                "free_download": false
            }
        ],
        "files": []
    },
    "meta": null,
    "error": null
}

The bytes of an image

GET /media/content read since 6.6.0

The bytes of one image from the upload folder, so a client can edit it: crop it, rotate it, and send it back.

This is the one route that does not answer with the envelope. It answers with the file, and with the cross-origin header that lets a browser read it into a canvas, which the shop's own image URLs do not carry. Without it, a web client cannot export an edited image at all.

A path that tries to leave the upload folder is refused rather than resolved.

Query

ParameterTypeDescription
pathrequiredstringThe image, relative to the upload folder.

Response

The content of the file requested, with the content type taken from its extension.

Errors

CodeHTTPMeans
not_found404No such file, or a path that tried to leave the upload folder.

Upload the value of a file field

POST /fields/{table}/{namekey}/file write since 6.6.0

Uploads the file that is the value of one of your own custom fields. Only the two field types that hold one accept it: ajaximage and ajaxfile.

The field is named rather than numbered, and the table says which kind of record it belongs to, so the same route serves a product field, a category field and a customer field.

Path

ParameterTypeDescription
tablerequiredstringWhich kind of record: `product`, `category`, `user`, `order`, `address`.
namekeyrequiredstringThe field, as `fields[].namekey` gives it.

Body

FieldTypeDescription
datarequiredstringThe bytes, base64 encoded.
namestringThe file name to store it under.

Response

FieldTypeDescription
pathstringRelative to the field's own upload folder. This is the value to store on the record.
namestringThe file name it was stored under.
urlstringAbsolute, where the shop serves it from.

Errors

CodeHTTPMeans
not_found404No such record kind.
bad_field400No such field, or it does not hold a file.
bad_type400The extension is not one this shop accepts.
write_failed500The upload folder refused the file.
POST/fields/product/test_ajax_image/file
curl -X POST "$SHOP/hikashop-api/v1/fields/product/test_ajax_image/file" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFAAH/q842iQAAAABJRU5ErkJggg==",
    "name": "documentation-capture-field.png"
}'
200success
{
    "data": {
        "path": "documentation-capture-field.png",
        "name": "documentation-capture-field.png",
        "url": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/documentation-capture-field.png"
    },
    "meta": null,
    "error": null
}

The shipping and payment methods an order can move to

GET /orders/{id}/methods read since 6.6.0

What this order could ship and be paid by, with what it uses now, so a change can be offered as a choice rather than as free text.

A method is identified by its plugin and its instance together, written plugin_id, which is the pairing the backend posts back. An order that ships from several warehouses carries one method per shipment, and multiple says so.

Path

ParameterTypeDescription
idrequiredintegerThe order.

Response

FieldTypeDescription
shippingobjectThe shipping choice.
currentstringWhat the order uses now, as plugin_id. _ when nothing is set, so the current one always matches an option.
multiplebooleanTrue when the order ships in several shipments and carries a method for each.
optionsobject[]What it could move to.
valuestringThe plugin_id pairing to send when changing the method.
labelstringAs the merchant named it.
groupsobject[]For an order that ships in several shipments, the shipments and the method chosen for each. Empty otherwise.
paymentobjectThe payment choice, in the same shape.
currentstringWhat the order uses now.
multiplebooleanAlways false: an order is paid one way.
optionsobject[]What it could move to.
valuestringThe plugin_id pairing to send.
labelstringAs the merchant named it.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not see it.
GET/orders/{id}/methods
curl "$SHOP/hikashop-api/v1/orders/{id}/methods" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "shipping": {
            "current": "manual_1",
            "multiple": false,
            "options": [
                {
                    "value": "manual_1",
                    "label": "Vendor 2 only shipping"
                },
                {
                    "value": "manual_3",
                    "label": "Express Test Flat Rate"
                },
                {
                    "value": "fedextest_2-1",
                    "label": "FedEx TEST - FedEx Ground"
                }
            ],
            "groups": []
        },
        "payment": {
            "current": "paypalcheckout_3",
            "multiple": false,
            "options": [
                {
                    "value": "banktransfer_1",
                    "label": "Bank transfer"
                },
                {
                    "value": "linepay_2",
                    "label": "LINE Pay"
                },
                {
                    "value": "paypalcheckout_3",
                    "label": "PayPal Checkout Express Test"
                }
            ]
        }
    },
    "meta": null,
    "error": null
}

Make an address the default

PUT /customers/{id}/addresses/{aid}/default write since 6.6.0

Marks one of a customer's addresses as their default, and answers with the customer as it now stands.

The default is per kind: an address used for billing becomes the default billing address, and the one it replaces stops being it. Nothing is sent in the body.

Path

ParameterTypeDescription
idrequiredintegerThe customer.
aidrequiredintegerThe address, from `addresses[].id`.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
PUT/customers/{id}/addresses/{id}/default
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}/default" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 20,
        "cms_id": 0,
        "name": "John Doe",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "",
        "type": "guest",
        "blocked": false,
        "can_edit_account": true,
        "groups_editable": false,
        "groups": [],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1783721823,
        "addresses": [
            {
                "id": 26,
                "types": [
                    "billing"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 25,
                "types": [
                    "shipping"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            }
        ],
        "orders": [
            {
                "id": 26,
                "number": "B2D6",
                "status": "created",
                "created": 1785338765,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 28,
                "number": "B2F8",
                "status": "confirmed",
                "created": 1783722444,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 30,
                "number": "ZBUNDLE",
                "status": "confirmed",
                "created": 1783722444,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 22,
                "number": "Z22",
                "status": "created",
                "created": 1783721823,
                "total": 14.99,
                "currency_id": 1
            }
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Create a characteristic or one of its values

POST /products/characteristics write since 6.6.0

Creates a characteristic, such as Size, or a value of one, such as XL.

Send a name for a characteristic, or a value with the parent_id of the characteristic it belongs to. They are the same records at two levels, which is why one route serves both.

Body

FieldTypeDescription
namestringThe name of a new characteristic. Give this or value.
valuestringThe name of a new value, with parent_id.
parent_idintegerThe characteristic a value belongs to.

Response

FieldTypeDescription
idintegerWhat was created, which is what a variant refers to.
valuestringIts name.
parent_idinteger0 for a characteristic, the characteristic for a value.

Errors

CodeHTTPMeans
invalid_request400Neither a name nor a value was given.
save_failed500The shop refused to save it.
POST/products/characteristics
curl -X POST "$SHOP/hikashop-api/v1/products/characteristics" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Documentation capture"
}'
200success
{
    "data": {
        "id": 36,
        "value": "Documentation capture",
        "parent_id": 0
    },
    "meta": null,
    "error": null
}

Create a product category

POST /products/categories write since 6.6.0

Creates a category under another, or at the top of the product tree when no parent is given.

Body

FieldTypeDescription
namerequiredstringIts name.
parent_idintegerThe category to put it under. Omit for the top of the tree.
publishedbooleanDefaults to published.
descriptionstringThe long description, as HTML.
meta_descriptionstringSEO description.
accessobjectWho may see it.
custom_fieldsobjectYour own category fields, keyed by namekey.

Response

FieldTypeDescription
idintegerThe category that was created.
namestring
parent_idintegerWhere it sits.
publishedboolean

Errors

CodeHTTPMeans
invalid_fields400One of your own fields was rejected by its own rules.
save_failed500The shop refused to save it.
POST/products/categories
curl -X POST "$SHOP/hikashop-api/v1/products/categories" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Documentation capture"
}'
200success
{
    "data": {
        "id": 253,
        "name": "Documentation capture",
        "parent_id": 2,
        "published": true
    },
    "meta": null,
    "error": null
}

Create a manufacturer

POST /products/manufacturers write since 6.6.0

Creates a brand. HikaShop keeps manufacturers in the category tree under their own root, so this takes the same fields as a category and answers the same way.

Body

FieldTypeDescription
namerequiredstringThe brand name.
parent_idintegerA manufacturer to nest it under, which most shops do not use.
publishedbooleanDefaults to published.
descriptionstringThe long description, as HTML.
meta_descriptionstringSEO description.
accessobjectWho may see it.
custom_fieldsobjectYour own category fields.

Response

FieldTypeDescription
idintegerThe manufacturer that was created, which is what manufacturer_id on a product stores.
namestring
parent_idintegerIts root.
publishedboolean

Errors

CodeHTTPMeans
invalid_fields400One of your own fields was rejected by its own rules.
save_failed500The shop refused to save it.
POST/products/manufacturers
curl -X POST "$SHOP/hikashop-api/v1/products/manufacturers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Documentation capture brand"
}'
200success
{
    "data": {
        "id": 254,
        "name": "Documentation capture brand",
        "parent_id": 10,
        "published": true
    },
    "meta": null,
    "error": null
}

Update a category

PUT /categories/{id} write since 6.6.0

Changes the fields you send and leaves the rest alone. It serves manufacturers and the other trees as well, since they are all categories.

Path

ParameterTypeDescription
idrequiredintegerThe category.

Body

Any of name, parent_id, published, description, meta_description, access and custom_fields. What you do not send keeps its value.

Response

FieldTypeDescription
idstring
fieldsobject[]The definitions of your own category fields.
namekeystringThe key its value is stored under.
labelstringTranslated into the operator's language.
typestringWhat to render: text, radio, singledropdown, file, and the rest.
raw_typestringHikaShop's own name for the type.
requiredbooleanWhether the shop refuses to save without it.
defaultstringThe value used when none is given.
optionsobject[]The choices, for a field that has them.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanWhether its value can be translated.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
namestring
parent_idintegerIts parent.
typestringWhich tree it belongs to: product, manufacturer, tax, and so on.
descriptionstringThe long description, as HTML.
meta_descriptionstringSEO description.
publishedboolean
accessobjectWho may see it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
imagestring|nullAbsolute URL of its image, null when it has none.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such category, or the operator may not change it.
invalid_fields400One of your own fields was rejected by its own rules.
PUT/categories/{id}
curl -X PUT "$SHOP/hikashop-api/v1/categories/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Shampoo"
}'
200success
{
    "data": {
        "fields": [],
        "id": 252,
        "name": "Shampoo",
        "parent_id": 220,
        "type": "product",
        "description": "",
        "meta_description": "",
        "published": true,
        "access": {
            "mode": "all",
            "groups": []
        },
        "image": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-cat-252.png",
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Delete a category

DELETE /categories/{id} write since 6.6.0

Deletes a category. The products in it are not deleted; they simply stop being in it, and a product left in no category at all disappears from the shop's listings.

Path

ParameterTypeDescription
idrequiredintegerThe category.

Response

FieldTypeDescription
idintegerThe category that was deleted.
deletedbooleanTrue when the row is gone.

Errors

CodeHTTPMeans
not_found404No such category, or the operator may not delete it.
DELETE/categories/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/categories/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 255,
        "deleted": true
    },
    "meta": null,
    "error": null
}

Read one discount or coupon

GET /discounts/{id} read since 6.6.0

One reduction with every restriction it carries.

Path

ParameterTypeDescription
idrequiredintegerThe discount or coupon.

Response

FieldTypeDescription
idinteger
typestringdiscount applies by itself, coupon waits for its code.
codestringWhat the customer types. Empty on a discount.
kindstringWhether the value is a percentage or a fixed amount.
valuenumberThe reduction, read according to kind.
currency_idintegerThe currency a fixed amount is in.
publishedboolean
startinteger|nullUnix timestamp before which it does not apply.
endinteger|nullUnix timestamp after which it expires.
minimum_ordernumberOrder total below which it does not apply, 0 for none.
maximum_ordernumberOrder total above which it stops applying, 0 for none.
quotaintegerHow many times it may be used in total, 0 for no limit.
quota_per_userintegerHow many times one customer may use it, 0 for no limit.
used_timesintegerHow many times it already has been.
tax_includedbooleanWhether the value is understood as tax included.
tax_idintegerThe tax category of the reduction itself.
shipping_percentnumberA reduction on the shipping rather than on the goods.
minimum_productsintegerFewest items in the cart for it to apply.
maximum_productsintegerMost items for it to still apply.
product_idsinteger[]Restricted to these products. Empty means all of them.
exclude_product_idsinteger[]Never applies to these.
category_idsinteger[]Restricted to these categories.
category_childsbooleanWhether those categories include their sub-categories.
exclude_category_idsinteger[]Never applies in these categories.
exclude_category_childsbooleanWhether those exclusions include sub-categories.
zone_idsinteger[]Restricted to these zones.
user_idsinteger[]Restricted to these customers.
accessobjectWhich user groups it is for, in the usual mode and groups shape.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
exclude_accessobjectWhich user groups it is never for.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
auto_loadbooleanFor a coupon, whether the shop applies it without the customer typing it.
product_onlybooleanWhether it reduces only the goods and leaves the fees alone.
discounted_productsintegerHow many products in the cart it applied to, on a discount that has been used.

Errors

CodeHTTPMeans
not_found404No such discount, or the operator may not see it.
GET/discounts/1
curl "$SHOP/hikashop-api/v1/discounts/1" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 1,
        "type": "coupon",
        "code": "APPTEST10",
        "kind": "flat",
        "value": 10,
        "currency_id": 1,
        "published": true,
        "start": 0,
        "end": 0,
        "minimum_order": 0,
        "maximum_order": 0,
        "quota": 0,
        "quota_per_user": 0,
        "used_times": 0,
        "tax_included": false,
        "tax_id": 0,
        "shipping_percent": 0,
        "minimum_products": 0,
        "maximum_products": 0,
        "product_ids": [],
        "exclude_product_ids": [],
        "category_ids": [],
        "category_childs": false,
        "exclude_category_ids": [],
        "exclude_category_childs": false,
        "zone_ids": [],
        "user_ids": [],
        "access": {
            "mode": "all",
            "groups": []
        },
        "exclude_access": {
            "mode": "none",
            "groups": []
        },
        "auto_load": false,
        "product_only": false,
        "discounted_products": 0
    },
    "meta": null,
    "error": null
}

Create a discount or a coupon

POST /discounts write since 6.6.0

Creates a reduction and answers with it as the shop stored it.

type decides which kind it is: a coupon needs a code, a discount applies by itself. Either way value is required, read according to kind.

Body

Any field of a discount. type and value are required, and a coupon also needs a code. The restriction lists take ids, and an empty list means no restriction of that kind.

Response

FieldTypeDescription
idinteger
typestringdiscount applies by itself, coupon waits for its code.
codestringWhat the customer types. Empty on a discount.
kindstringWhether the value is a percentage or a fixed amount.
valuenumberThe reduction, read according to kind.
currency_idintegerThe currency a fixed amount is in.
publishedboolean
startinteger|nullUnix timestamp before which it does not apply.
endinteger|nullUnix timestamp after which it expires.
minimum_ordernumberOrder total below which it does not apply, 0 for none.
maximum_ordernumberOrder total above which it stops applying, 0 for none.
quotaintegerHow many times it may be used in total, 0 for no limit.
quota_per_userintegerHow many times one customer may use it, 0 for no limit.
used_timesintegerHow many times it already has been.
tax_includedbooleanWhether the value is understood as tax included.
tax_idintegerThe tax category of the reduction itself.
shipping_percentnumberA reduction on the shipping rather than on the goods.
minimum_productsintegerFewest items in the cart for it to apply.
maximum_productsintegerMost items for it to still apply.
product_idsinteger[]Restricted to these products. Empty means all of them.
exclude_product_idsinteger[]Never applies to these.
category_idsinteger[]Restricted to these categories.
category_childsbooleanWhether those categories include their sub-categories.
exclude_category_idsinteger[]Never applies in these categories.
exclude_category_childsbooleanWhether those exclusions include sub-categories.
zone_idsinteger[]Restricted to these zones.
user_idsinteger[]Restricted to these customers.
accessobjectWhich user groups it is for, in the usual mode and groups shape.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
exclude_accessobjectWhich user groups it is never for.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
auto_loadbooleanFor a coupon, whether the shop applies it without the customer typing it.
product_onlybooleanWhether it reduces only the goods and leaves the fees alone.
discounted_productsintegerHow many products in the cart it applied to, on a discount that has been used.

Errors

CodeHTTPMeans
code_required400A coupon needs a code.
code_taken400Another coupon already uses that code.
value_required400A reduction needs a value.
not_found404Not reachable when creating: the same handler serves the update, where it means no such discount.
save_failed500The shop refused to save it.
POST/discounts
curl -X POST "$SHOP/hikashop-api/v1/discounts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "coupon",
    "code": "DOCCAPTURE",
    "kind": "percent",
    "value": 10
}'
200success
{
    "data": {
        "id": 488,
        "type": "coupon",
        "code": "DOCCAPTURE",
        "kind": "percent",
        "value": 10,
        "currency_id": 0,
        "published": true,
        "start": 0,
        "end": 0,
        "minimum_order": 0,
        "maximum_order": 0,
        "quota": 0,
        "quota_per_user": 0,
        "used_times": 0,
        "tax_included": false,
        "tax_id": 0,
        "shipping_percent": 0,
        "minimum_products": 0,
        "maximum_products": 0,
        "product_ids": [],
        "exclude_product_ids": [],
        "category_ids": [],
        "category_childs": false,
        "exclude_category_ids": [],
        "exclude_category_childs": false,
        "zone_ids": [],
        "user_ids": [],
        "access": {
            "mode": "all",
            "groups": []
        },
        "exclude_access": {
            "mode": "none",
            "groups": []
        },
        "auto_load": false,
        "product_only": false,
        "discounted_products": 0
    },
    "meta": null,
    "error": null
}

Update a discount or a coupon

PUT /discounts/{id} write since 6.6.0

Changes the fields you send and leaves the rest alone, and answers with the reduction as it now stands.

Path

ParameterTypeDescription
idrequiredintegerThe discount or coupon.

Body

Any field of a discount. What you do not send keeps its value. A restriction list you do send replaces the one it had.

Response

FieldTypeDescription
idinteger
typestringdiscount applies by itself, coupon waits for its code.
codestringWhat the customer types. Empty on a discount.
kindstringWhether the value is a percentage or a fixed amount.
valuenumberThe reduction, read according to kind.
currency_idintegerThe currency a fixed amount is in.
publishedboolean
startinteger|nullUnix timestamp before which it does not apply.
endinteger|nullUnix timestamp after which it expires.
minimum_ordernumberOrder total below which it does not apply, 0 for none.
maximum_ordernumberOrder total above which it stops applying, 0 for none.
quotaintegerHow many times it may be used in total, 0 for no limit.
quota_per_userintegerHow many times one customer may use it, 0 for no limit.
used_timesintegerHow many times it already has been.
tax_includedbooleanWhether the value is understood as tax included.
tax_idintegerThe tax category of the reduction itself.
shipping_percentnumberA reduction on the shipping rather than on the goods.
minimum_productsintegerFewest items in the cart for it to apply.
maximum_productsintegerMost items for it to still apply.
product_idsinteger[]Restricted to these products. Empty means all of them.
exclude_product_idsinteger[]Never applies to these.
category_idsinteger[]Restricted to these categories.
category_childsbooleanWhether those categories include their sub-categories.
exclude_category_idsinteger[]Never applies in these categories.
exclude_category_childsbooleanWhether those exclusions include sub-categories.
zone_idsinteger[]Restricted to these zones.
user_idsinteger[]Restricted to these customers.
accessobjectWhich user groups it is for, in the usual mode and groups shape.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
exclude_accessobjectWhich user groups it is never for.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
auto_loadbooleanFor a coupon, whether the shop applies it without the customer typing it.
product_onlybooleanWhether it reduces only the goods and leaves the fees alone.
discounted_productsintegerHow many products in the cart it applied to, on a discount that has been used.

Errors

CodeHTTPMeans
not_found404No such discount, or the operator may not change it.
code_required400A coupon needs a code, so it cannot be cleared on one.
code_taken400Another coupon already uses that code.
value_required400A reduction needs a value.
save_failed500The shop refused to save it.
PUT/discounts/1
curl -X PUT "$SHOP/hikashop-api/v1/discounts/1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "flat",
    "value": 10
}'
200success
{
    "data": {
        "id": 1,
        "type": "coupon",
        "code": "APPTEST10",
        "kind": "flat",
        "value": 10,
        "currency_id": 1,
        "published": true,
        "start": 0,
        "end": 0,
        "minimum_order": 0,
        "maximum_order": 0,
        "quota": 0,
        "quota_per_user": 0,
        "used_times": 0,
        "tax_included": false,
        "tax_id": 0,
        "shipping_percent": 0,
        "minimum_products": 0,
        "maximum_products": 0,
        "product_ids": [],
        "exclude_product_ids": [],
        "category_ids": [],
        "category_childs": false,
        "exclude_category_ids": [],
        "exclude_category_childs": false,
        "zone_ids": [],
        "user_ids": [],
        "access": {
            "mode": "all",
            "groups": []
        },
        "exclude_access": {
            "mode": "none",
            "groups": []
        },
        "auto_load": false,
        "product_only": false,
        "discounted_products": 0
    },
    "meta": null,
    "error": null
}

Delete a discount or a coupon

DELETE /discounts/{id} write since 6.6.0

Deletes a reduction. Orders that already used it keep the amount they were given: the discount on an order is a figure, not a pointer at this row.

Path

ParameterTypeDescription
idrequiredintegerThe discount or coupon.

Response

FieldTypeDescription
deletedbooleanTrue when the row is gone.

Errors

CodeHTTPMeans
not_found404No such discount, or the operator may not delete it.
DELETE/discounts/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/discounts/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "deleted": 489
    },
    "meta": null,
    "error": null
}

Create a customer

POST /customers write since 6.6.0

Creates a customer from an email address and a name, as a guest: no account, no password, nothing for them to log in with.

POST /customers/{id}/account turns one into a registered customer afterwards.

Body

FieldTypeDescription
emailrequiredstringMust not belong to another customer.
namestringTheir display name.

Response

FieldTypeDescription
idintegerThe customer that was created.

Errors

CodeHTTPMeans
email_taken400Another customer already has that address.
save_failed500The shop refused to save it.
POST/customers
curl -X POST "$SHOP/hikashop-api/v1/customers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
    "name": "Alex Marchand"
}'
200success
{
    "data": {
        "id": 5275
    },
    "meta": null,
    "error": null
}

Attach a downloadable file

POST /products/{id}/files write since 6.6.0

The same as attaching an image, for the files a customer downloads after buying. free_download decides whether they have to buy it first.

Path

ParameterTypeDescription
idrequiredintegerThe product.

Body

FieldTypeDescription
datastringThe bytes, base64 encoded. Give this or path.
pathstringA file already in the upload folder.
namestringThe file name to store it under.
descriptionstringA note on the file.
accessobjectWho may download it.

Response

FieldTypeDescription
idinteger
namestring
pathstringRelative to the upload folder.
urlstringAbsolute, ready to display.
orderingintegerWhere it sits among the others; the first image is the one the shop shows.
descriptionstringThe alt text for an image, or a note on a file.
accessobjectWho may see or download it.
modestringall, none, or groups when it is restricted to some.
groupsinteger[]User **group** ids, and not Joomla view levels. The two id spaces overlap and disagree, so a value that looks plausible can grant the wrong audience.
free_downloadbooleanFiles only: whether it can be downloaded without buying the product.

Errors

CodeHTTPMeans
not_found404No such product, or the operator may not change it.
bad_type400The extension is not one this shop accepts.
write_failed500The upload folder refused the file.
POST/products/{id}/files
curl -X POST "$SHOP/hikashop-api/v1/products/{id}/files" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nD3HsQ2AMAxE0T5TXA9IjhOSeAAkKmpLLIAoEAWK2F9wQKK5e/qXcMYKUdE4RSxHTXjuvet3D1CNPqBQZmxbKimw5jJUOJyzcSSJifKPZJa5ffwAAP//AwBQSwMEFAAGAAgAAAAhAA==",
    "name": "documentation-capture.pdf",
    "description": "A small file"
}'
200success
{
    "data": {
        "id": 8327,
        "name": "documentation-capture.pdf",
        "path": "documentation-capture.pdf",
        "url": "",
        "ordering": 1,
        "description": "A small file",
        "access": {
            "mode": "all",
            "groups": []
        },
        "free_download": false
    },
    "meta": null,
    "error": null
}

A blank address form

GET /customers/{id}/addresses read since 6.6.0

The address form of a customer: the shop's own address fields, and the values of the address asked for. Without an address id it is a blank form, which is what you draw to add one.

The form comes with the values because a shop decides its own address fields, so build from fields rather than assuming a shape.

Path

ParameterTypeDescription
idrequiredintegerThe customer.

Response

FieldTypeDescription
address_idintegerThe address this form is for, 0 for a new one.
typesstring[]Which of billing and shipping it is used for.
defaultstring|nullWhich kind it is the default for, null when it is not a default.
fieldsobject[]The shop's address fields, in display order.
namekeystringThe key its value is stored under.
typestringWhat to render: text, radio, singledropdown, file, and the rest.
raw_typestringHikaShop's own name for the type.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save without it.
optionsobject[]The choices, for a field that has them.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanWhether its value can be translated.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
valuesobjectThe current values, keyed by field namekey. Empty on a blank form. Keyed by whatever address fields this shop has.
country_namestringThe country spelled out, since the value is a zone namekey.
state_namestringThe state spelled out, empty where the country has none.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not see them.
GET/customers/{id}/addresses
curl "$SHOP/hikashop-api/v1/customers/{id}/addresses" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "address_id": 0,
        "types": [
            "billing"
        ],
        "default": false,
        "fields": [
            {
                "namekey": "address_title",
                "type": "singledropdown",
                "raw_type": "singledropdown",
                "label": "Title",
                "default": "",
                "required": true,
                "options": [
                    {
                        "value": "Mr",
                        "label": "Mr",
                        "label_key": "HIKA_TITLE_MR"
                    },
                    {
                        "value": "Mrs",
                        "label": "Mrs",
                        "label_key": "HIKA_TITLE_MRS"
                    },
                    {
                        "value": "Miss",
                        "label": "Miss",
                        "label_key": "HIKA_TITLE_MISS"
                    },
                    {
                        "value": "Ms",
                        "label": "Ms",
                        "label_key": "HIKA_TITLE_MS"
                    },
                    "… 1 more, trimmed for the example"
                ],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_firstname",
                "type": "text",
                "raw_type": "text",
                "label": "First name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_lastname",
                "type": "text",
                "raw_type": "text",
                "label": "Last name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_company",
                "type": "text",
                "raw_type": "text",
                "label": "Company",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            "… 7 more, trimmed for the example"
        ],
        "values": {
            "address_title": "Mr",
            "address_firstname": "Alex",
            "address_lastname": "Marchand",
            "address_company": "Lilas SARL",
            "address_street": "12 rue des Lilas",
            "address_post_code": "44000",
            "address_city": "Nantes",
            "address_telephone": "+33 2 00 00 00 00",
            "address_country": "",
            "address_state": "",
            "address_vat": "FR00000000000"
        },
        "country_name": "",
        "state_name": ""
    },
    "meta": null,
    "error": null
}

An address with its form

GET /customers/{customerId}/addresses/{addressId} read since 6.6.0

The address form of a customer: the shop's own address fields, and the values of the address asked for. Without an address id it is a blank form, which is what you draw to add one.

The form comes with the values because a shop decides its own address fields, so build from fields rather than assuming a shape.

Path

ParameterTypeDescription
customerIdrequiredintegerThe customer.
addressIdrequiredintegerThe address.

Response

FieldTypeDescription
address_idintegerThe address this form is for, 0 for a new one.
typesstring[]Which of billing and shipping it is used for.
defaultstring|nullWhich kind it is the default for, null when it is not a default.
fieldsobject[]The shop's address fields, in display order.
namekeystringThe key its value is stored under.
typestringWhat to render: text, radio, singledropdown, file, and the rest.
raw_typestringHikaShop's own name for the type.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save without it.
optionsobject[]The choices, for a field that has them.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanWhether its value can be translated.
upload_dirstringFor a file field, where its uploads are kept.
allowed_extensionsstringFor a file field, the extensions it accepts. Empty means the shop default.
date_formatstringFor a date field, the format it is stored in.
valuesobjectThe current values, keyed by field namekey. Empty on a blank form. Keyed by whatever address fields this shop has.
country_namestringThe country spelled out, since the value is a zone namekey.
state_namestringThe state spelled out, empty where the country has none.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not see them.
GET/customers/{id}/addresses/{id}
curl "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "address_id": 12,
        "types": [
            "billing"
        ],
        "default": true,
        "fields": [
            {
                "namekey": "address_title",
                "type": "singledropdown",
                "raw_type": "singledropdown",
                "label": "Title",
                "default": "",
                "required": true,
                "options": [
                    {
                        "value": "Mr",
                        "label": "Mr",
                        "label_key": "HIKA_TITLE_MR"
                    },
                    {
                        "value": "Mrs",
                        "label": "Mrs",
                        "label_key": "HIKA_TITLE_MRS"
                    },
                    {
                        "value": "Miss",
                        "label": "Miss",
                        "label_key": "HIKA_TITLE_MISS"
                    },
                    {
                        "value": "Ms",
                        "label": "Ms",
                        "label_key": "HIKA_TITLE_MS"
                    },
                    "… 1 more, trimmed for the example"
                ],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_firstname",
                "type": "text",
                "raw_type": "text",
                "label": "First name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_lastname",
                "type": "text",
                "raw_type": "text",
                "label": "Last name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_company",
                "type": "text",
                "raw_type": "text",
                "label": "Company",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            "… 7 more, trimmed for the example"
        ],
        "values": {
            "address_title": "Mr",
            "address_firstname": "Alex",
            "address_lastname": "Marchand",
            "address_company": "Lilas SARL",
            "address_street": "12 rue des Lilas",
            "address_post_code": "44000",
            "address_city": "Nantes",
            "address_telephone": "+33 2 00 00 00 00",
            "address_country": "country_Albania_2",
            "address_state": "state_Beratit_274",
            "address_vat": "FR00000000000"
        },
        "country_name": "Shqipëria",
        "state_name": "Beratit"
    },
    "meta": null,
    "error": null
}

Add an address

POST /customers/{id}/addresses write since 6.6.0

Writes an address of a customer. Without an address id it creates one; with one it saves that one.

types says what the address is for, billing or shipping or both, and default makes it the default for those. An address is validated by the shop's own field rules, so a missing required field is refused rather than half-saved.

Path

ParameterTypeDescription
idrequiredintegerThe customer.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by address field namekey, the same keys the form returned in values.
typesstring[]Which of billing and shipping this address is for. Defaults to both.
defaultbooleanMake it the default for its types.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
invalid_fields400A field was rejected by its own rules.
invalid_address400The address is not one the shop will accept, usually a missing required field.
POST/customers/{id}/addresses
curl -X POST "$SHOP/hikashop-api/v1/customers/{id}/addresses" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
        "address_title": "Mr",
        "address_firstname": "Alex",
        "address_lastname": "Marchand",
        "address_street": "12 rue des Lilas",
        "address_post_code": "44000",
        "address_city": "Nantes",
        "address_telephone": "+33 2 00 00 00 00",
        "address_country": "country_Albania_2",
        "address_state": "state_Beratit_274"
    }
}'
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 24,
                "types": [
                    "billing"
                ],
                "name": "John Doe",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 1 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Add an address

PUT /customers/{id}/addresses write since 6.6.0

Writes an address of a customer. Without an address id it creates one; with one it saves that one.

types says what the address is for, billing or shipping or both, and default makes it the default for those. An address is validated by the shop's own field rules, so a missing required field is refused rather than half-saved.

Path

ParameterTypeDescription
idrequiredintegerThe customer.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by address field namekey, the same keys the form returned in values.
typesstring[]Which of billing and shipping this address is for. Defaults to both.
defaultbooleanMake it the default for its types.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
invalid_fields400A field was rejected by its own rules.
invalid_address400The address is not one the shop will accept, usually a missing required field.
PUT/customers/{id}/addresses
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}/addresses" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
        "address_title": "Mr",
        "address_firstname": "Alex",
        "address_lastname": "Marchand",
        "address_street": "12 rue des Lilas",
        "address_post_code": "44000",
        "address_city": "Nantes",
        "address_telephone": "+33 2 00 00 00 00",
        "address_country": "country_Albania_2",
        "address_state": "state_Beratit_274"
    }
}'
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4369,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 2 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Save an address

POST /customers/{customerId}/addresses/{addressId} write since 6.6.0

Writes an address of a customer. Without an address id it creates one; with one it saves that one.

types says what the address is for, billing or shipping or both, and default makes it the default for those. An address is validated by the shop's own field rules, so a missing required field is refused rather than half-saved.

Path

ParameterTypeDescription
customerIdrequiredintegerThe customer.
addressIdrequiredintegerThe address.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by address field namekey, the same keys the form returned in values.
typesstring[]Which of billing and shipping this address is for. Defaults to both.
defaultbooleanMake it the default for its types.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
invalid_fields400A field was rejected by its own rules.
invalid_address400The address is not one the shop will accept, usually a missing required field.
POST/customers/{id}/addresses/{id}
curl -X POST "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
        "address_title": "Mr",
        "address_firstname": "Alex",
        "address_lastname": "Marchand",
        "address_street": "12 rue des Lilas",
        "address_post_code": "44000",
        "address_city": "Nantes",
        "address_telephone": "+33 2 00 00 00 00",
        "address_country": "country_Albania_2",
        "address_state": "state_Beratit_274"
    }
}'
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4369,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 2 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Save an address

PUT /customers/{customerId}/addresses/{addressId} write since 6.6.0

Writes an address of a customer. Without an address id it creates one; with one it saves that one.

types says what the address is for, billing or shipping or both, and default makes it the default for those. An address is validated by the shop's own field rules, so a missing required field is refused rather than half-saved.

Path

ParameterTypeDescription
customerIdrequiredintegerThe customer.
addressIdrequiredintegerThe address.

Body

FieldTypeDescription
fieldsrequiredobjectKeyed by address field namekey, the same keys the form returned in values.
typesstring[]Which of billing and shipping this address is for. Defaults to both.
defaultbooleanMake it the default for its types.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
invalid_fields400A field was rejected by its own rules.
invalid_address400The address is not one the shop will accept, usually a missing required field.
PUT/customers/{id}/addresses/{id}
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
        "address_title": "Mr",
        "address_firstname": "Alex",
        "address_lastname": "Marchand",
        "address_street": "12 rue des Lilas",
        "address_post_code": "44000",
        "address_city": "Nantes",
        "address_telephone": "+33 2 00 00 00 00",
        "address_country": "country_Albania_2",
        "address_state": "state_Beratit_274"
    }
}'
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4369,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 2 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Delete an address

DELETE /customers/{id}/addresses write since 6.6.0

Removes an address from a customer. Orders that used it keep their own copy of it.

Path

ParameterTypeDescription
idrequiredintegerThe customer.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
DELETE/customers/{id}/addresses/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4369,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 2 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Delete an address

DELETE /customers/{customerId}/addresses/{addressId} write since 6.6.0

Removes an address from a customer. Orders that used it keep their own copy of it.

Path

ParameterTypeDescription
customerIdrequiredintegerThe customer.
addressIdrequiredintegerThe address.

Response

FieldTypeDescription
idintegerThe HikaShop customer id, which is what every customer route takes.
cms_idintegerThe Joomla or WordPress user id, 0 for a guest with no account.
namestring
emailstring
usernamestringThe login, empty for a guest.
typestringregistered or guest.
blockedbooleanWhether the CMS account is disabled.
can_edit_accountbooleanWhether this operator may change the login and password of this particular account. False for an account above their own level, which is how a super user is protected from staff.
groups_editablebooleanWhether this operator may change which groups the customer is in.
groupsobject[]The groups they are in.
idinteger
titlestring
available_groupsobject[]Every group, with whether this operator may put the customer into it, so a picker can grey out the rest rather than offering a refusal.
idinteger
titlestring
assignablebooleanFalse for a group this operator may not grant.
createdintegerUnix timestamp of the first time the shop saw them.
addressesobject[]Their addresses, defaults first.
idinteger
typesstring[]Which of billing and shipping it is used for.
namestring
companystring
streetstring
citystring
post_codestring
telephonestring
defaultbooleanWhether it is the default for one of its types.
formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
textstringSeveral lines, for an invoice or a label.
one_linestringOne line, for a list.
ordersobject[]Their orders, newest first, enough to list them.
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
totalnumberTax included, in the order currency.
currency_idintegerThat currency.
fieldsobject[]The definitions of your own customer fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.

Errors

CodeHTTPMeans
not_found404No such customer or address, or the operator may not change them.
DELETE/customers/{id}/addresses/{id}
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 14,
        "cms_id": 156,
        "name": "Admin",
        "email": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
        "username": "admin",
        "type": "registered",
        "blocked": false,
        "can_edit_account": false,
        "groups_editable": false,
        "groups": [
            {
                "id": 8,
                "title": "Mr"
            }
        ],
        "available_groups": [
            {
                "id": 1,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 9,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 6,
                "title": "Mr",
                "assignable": false
            },
            {
                "id": 7,
                "title": "Mr",
                "assignable": false
            },
            "… 5 more, trimmed for the example"
        ],
        "created": 1778653121,
        "addresses": [
            {
                "id": 13,
                "types": [
                    "shipping"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 12,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": true,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4369,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            {
                "id": 4368,
                "types": [
                    "billing"
                ],
                "name": "Test Buyer",
                "company": "Lilas SARL",
                "street": "12 rue des Lilas",
                "city": "Nantes",
                "post_code": "44000",
                "telephone": "+33 2 00 00 00 00",
                "default": false,
                "formatted": {
                    "text": "Alex Marchand\r\n12 rue des Lilas\r\n44000 Nantes\r\nFrance",
                    "one_line": "Marchand Alex - 12 rue des Lilas, Nantes (France)"
                }
            },
            "… 2 more, trimmed for the example"
        ],
        "orders": [
            {
                "id": 20,
                "number": "X20",
                "status": "confirmed",
                "created": 1783721323,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 18,
                "number": "U18",
                "status": "confirmed",
                "created": 1783347376,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 16,
                "number": "S16",
                "status": "created",
                "created": 1783345950,
                "total": 14.99,
                "currency_id": 1
            },
            {
                "id": 2,
                "number": "TEST-VOTE-1",
                "status": "confirmed",
                "created": 1778653148,
                "total": 100,
                "currency_id": 1
            },
            "… 96 more, trimmed for the example"
        ],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": []
    },
    "meta": null,
    "error": null
}

Redeem a pairing code

POST /pair public since 6.6.0

Exchanges a single-use code, generated in System > App Devices of the shop's backend, for the device's own token. This is the only route that answers without one.

The token comes back once. Only its hash is stored, so a lost token is re-paired, never recovered. Attempts are rate limited per address.

Body

FieldTypeDescription
coderequiredstringThe pairing code as shown in the backend. Case and spacing are normalised, so the grouping dash is optional.
device_namestringWhat to call this device in the device list. Defaults to Device.
platformstringA free label kept for the listing. Anything that is not a letter, a digit, a dash or an underscore is stripped.

Response

FieldTypeDescription
tokenstringSend this as the bearer token from now on. It is not retrievable again.
scopesstring[]read, and write when the code granted it.
device_idintegerThe row in the device list, which is what you revoke later.

Errors

CodeHTTPMeans
invalid_request400No code was sent.
invalid_code403The code is unknown, already used, or expired.
too_many_requests429Too many attempts from this address.
POST/pair
curl -X POST "$SHOP/hikashop-api/v1/pair" \
  -H "Content-Type: application/json" \
  -d '{
    "device_name": "Counter tablet",
    "platform": "android",
    "code": "7E9A0A"
}'
200success
{
    "data": {
        "token": "hk_dev_3f9c1a……",
        "scopes": [
            "read",
            "write"
        ],
        "device_id": 88
    },
    "meta": null,
    "error": null
}

List products

GET /products read since 6.6.0

The catalogue as the backend sees it, unpublished products included, which is what makes this different from the front end.

Variants are not listed on their own. A parent carries has_variants, and GET /products/{id} returns the variants themselves.

Query

ParameterTypeDescription
startintegerOffset into the result set. Defaults to 0.
limitintegerPage size. Defaults to 20 and is capped at 100.
searchstringMatches the product name and the product code.
idsstringComma separated product ids, to resolve a known set in one call.
category_idintegerRestrict to one category.

Response a list

FieldTypeDescription
idinteger
namestring
codestringThe SKU. Unique within the shop.
quantityinteger-1 when the product does not track stock, which is not the same as 0.
publishedboolean
has_variantsbooleanAsk GET /products/{id} for the variants themselves.
imagestring|nullAbsolute URL of the main image, or null.
pricenumber|nullnull when the product has no price row at all.
currency_idintegerA row in the shop's currency table, not an ISO code.
custom_fieldsobjectThe listing values of this product, keyed by field namekey. The keys are whatever this shop has configured, so there is no list to give. fields in the envelope says what they are.

Envelope meta

FieldTypeDescription
startintegerEchoes the offset used.
limitintegerEchoes the page size used.
totalintegerRows matching the filter, before paging. This is how you know there is another page.
fieldsobject[]The custom fields shown on listings, so a client can label the values it just received.
GET/products?limit=2
curl "$SHOP/hikashop-api/v1/products?limit=2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 8731,
            "name": "Anodised Film Camera — 35 mm",
            "code": "DEMO-0073",
            "quantity": 79,
            "published": true,
            "has_variants": false,
            "image": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0073.png",
            "price": 71.21,
            "currency_id": 1,
            "custom_fields": []
        },
        {
            "id": 8816,
            "name": "Anodised Headphones",
            "code": "DEMO-0133",
            "quantity": 14,
            "published": true,
            "has_variants": false,
            "image": "http://localhost:8080/apidoc_capture/images/com_hikashop/upload/demo-0133.png",
            "price": 317.13,
            "currency_id": 1,
            "custom_fields": []
        }
    ],
    "meta": {
        "start": 0,
        "limit": 2,
        "total": 306,
        "fields": []
    },
    "error": null
}

List orders

GET /orders read since 6.6.0

The orders the operator may see, newest first. It is the listing the app's order screen is built on, so it carries just enough to draw a row and no more; ask for one order when you need the rest.

Query

ParameterTypeDescription
startintegerOffset. Defaults to 0.
limitintegerDefaults to 20, capped at 100.
searchstringMatches the order number and the customer.
statusstringA status namekey, as listed by GET /statuses.

Response a list

FieldTypeDescription
idintegerThe order id, which is what every other order route takes.
numberstringThe order number the customer sees, which is not the id.
statusstringA namekey, not a label. GET /statuses translates it.
createdintegerUnix timestamp.
totalnumberWhat the customer owes, tax included, in the order currency.
currency_idintegerThe order keeps the currency it was placed in, which need not be the shop default.
customerobjectEnough to name the buyer in a list.
namestring
emailstring
custom_fieldsobjectThe listing values of your own order fields, keyed by namekey. The keys are whatever this shop has configured; fields in the envelope says what they are.

Envelope meta

FieldTypeDescription
startintegerEchoes the offset used.
limitintegerEchoes the page size used.
totalintegerOrders matching the filter, before paging.
fieldsobject[]The definitions behind custom_fields.
GET/orders?limit=2
curl "$SHOP/hikashop-api/v1/orders?limit=2" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 4000,
            "number": "DEMO00002",
            "status": "confirmed",
            "created": 1786173826,
            "total": 524.43,
            "currency_id": 1,
            "customer": {
                "name": null,
                "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
            },
            "custom_fields": []
        },
        {
            "id": 4002,
            "number": "DEMO00004",
            "status": "shipped",
            "created": 1786169963,
            "total": 437.95,
            "currency_id": 1,
            "customer": {
                "name": null,
                "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
            },
            "custom_fields": []
        }
    ],
    "meta": {
        "start": 0,
        "limit": 2,
        "total": 640,
        "fields": []
    },
    "error": null
}

Read one order

GET /orders/{id} read since 6.6.0

The whole order: its lines, its totals, its addresses, its history and your own fields.

Money is in the currency the order was placed in, which is not necessarily the shop's. Do not convert it: an order is a record of what was agreed at the time.

Path

ParameterTypeDescription
idrequiredintegerThe order id.

Response

FieldTypeDescription
idinteger
numberstringThe number the customer sees.
statusstringA namekey.
createdintegerUnix timestamp.
modifiedintegerUnix timestamp of the last change.
currency_idintegerThe currency the order was placed in.
totalsobjectThe figures. See the shape below.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.
customerobjectWho placed it.
namestring
emailstring
payment_methodstringHow it was paid, as the shop names it.
shipping_methodstringHow it ships.
invoice_numberstringEmpty until an invoice has been issued.
invoice_createdinteger|nullUnix timestamp of the invoice.
itemsobject[]The lines: id, name, code, quantity, price, tax and whether the line can still be edited.
idintegerThe line id, which is what PUT /orders/{id}/products/{lineId} takes. It is not the product id.
namestringThe product as it was named when ordered, which may since have changed.
codestringIts SKU at the time.
quantityinteger
pricenumberUnit price, tax excluded, as agreed at the time.
taxnumberTax on the line.
editablebooleanFalse once the line can no longer be changed, for instance on a shipped order.
billing_addressobject|nullThe address as it was at the time, null when there is none. It is a copy, not a pointer at the customer's current address.
shipping_addressobject|nullThe same, for delivery.
shipping_address_overridebooleanWhether the delivery address was set apart from the billing one.
historyobject[]What has happened to the order, oldest first.
statusstringThe namekey it moved to.
createdintegerUnix timestamp.
typestringWhat caused it: a payment notification, an operator, the shop itself.
reasonstringThe note recorded with the change, when there was one.
notifiedbooleanWhether the customer was emailed about it.
fieldsobject[]The definitions of your own order fields.
custom_fieldsobjectTheir values, keyed by namekey. The keys depend on the shop; fields says what they are.
custom_field_filesobjectFor a field holding a file, the file behind the value. Keyed the same way.
feesobjectThe discount, shipping and payment amounts, which is what PUT /orders/{id}/fees writes.
discountobjectIts amount, its tax, the tax_namekeys behind that tax, and the coupon code when one was used.
amountnumberA positive figure, already subtracted from the total.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
codestringThe coupon code, empty for a discount applied by hand.
shippingobjectThe shipping charge and what carried it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that handled it.
method_namestringAs the merchant named it.
paymentobjectThe payment fee and what took it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that took it.
method_namestringAs the merchant named it.
tax_ratesobject[]The rates that made up the tax, each with its namekey and rate, so a total can be explained rather than only shown.
namekeystringThe tax rate as the shop names it.
ratenumberAs a fraction, so 0.1 is ten percent.
GET/orders/{id}
curl "$SHOP/hikashop-api/v1/orders/{id}" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "id": 5630,
        "number": "S5630",
        "status": "confirmed",
        "created": 1738143180,
        "modified": 1786725619,
        "currency_id": 1,
        "totals": {
            "total": 313.92,
            "discount": 0,
            "shipping": 6.9,
            "payment": 0,
            "tax": 0
        },
        "customer": {
            "name": "Admin",
            "email": "This email address is being protected from spambots. You need JavaScript enabled to view it."
        },
        "payment_method": "paypalcheckout",
        "shipping_method": "manual",
        "invoice_number": "B5630",
        "invoice_created": 1738143180,
        "items": [
            {
                "id": 20630,
                "name": "Nordic Design item",
                "code": "SEED-7-44",
                "quantity": 1,
                "price": 307.02,
                "tax": 0,
                "editable": true
            },
            {
                "id": 20632,
                "name": "Test Product (Vendor 2)",
                "code": "TEST001",
                "quantity": 1,
                "price": 0,
                "tax": 0,
                "editable": true
            }
        ],
        "billing_address": null,
        "shipping_address": null,
        "shipping_address_override": [],
        "history": [],
        "fields": [],
        "custom_fields": [],
        "custom_field_files": [],
        "fees": {
            "discount": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "code": ""
            },
            "shipping": {
                "amount": 6.9,
                "tax": 0,
                "tax_namekeys": [],
                "method": "manual",
                "method_name": "Vendor 2 only shipping"
            },
            "payment": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "method": "paypalcheckout",
                "method_name": "PayPal Checkout Express Test"
            }
        },
        "tax_rates": [
            {
                "namekey": "apptest_vat10",
                "rate": 0.1
            },
            {
                "namekey": "apptest_vat20",
                "rate": 0.2
            },
            {
                "namekey": "gst",
                "rate": 0.06
            },
            {
                "namekey": "recargo 5,2%",
                "rate": 0.052
            },
            "… 1 more, trimmed for the example"
        ]
    },
    "meta": null,
    "error": null
}

Set the order fees

PUT /orders/{id}/fees write since 6.6.0

Replaces the discount, shipping and payment amounts and re-totals the order, so a client does not have to compute a total itself and risk disagreeing with the shop.

What you do not send keeps its current value.

Path

ParameterTypeDescription
idrequiredintegerThe order id.

Body

FieldTypeDescription
feesrequiredobjectAny of discount, shipping and payment, each an object with at least an amount, tax excluded. A discount is a positive figure and is subtracted.

Response

FieldTypeDescription
idinteger
feesobjectThe discount, shipping and payment amounts of the order.
discountobjectIts amount, its tax, the tax_namekeys behind that tax, and the coupon code when one was used.
amountnumberA positive figure, already subtracted from the total.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
codestringThe coupon code, empty for a discount applied by hand.
shippingobjectThe shipping charge and what carried it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that handled it.
method_namestringAs the merchant named it.
paymentobjectThe payment fee and what took it.
amountnumberTax excluded.
taxnumberThe tax on it.
tax_namekeysstring[]Which tax rates that came from.
methodstringThe plugin that took it.
method_namestringAs the merchant named it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
totalnumberWhat the customer owes, tax included.
discountnumberThe discount applied, as a positive figure already subtracted.
shippingnumberThe shipping charged.
paymentnumberThe payment fee charged.
taxnumberThe tax within the total, not on top of it.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not change it.
save_failed500The order could not be saved.
PUT/orders/{id}/fees
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/fees" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fees": {
        "shipping": {
            "amount": 4.9
        }
    }
}'
200success
{
    "data": {
        "id": 5630,
        "fees": {
            "discount": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "code": ""
            },
            "shipping": {
                "amount": 4.9,
                "tax": 0,
                "tax_namekeys": [],
                "method": "manual",
                "method_name": "Vendor 2 only shipping"
            },
            "payment": {
                "amount": 0,
                "tax": 0,
                "tax_namekeys": [],
                "method": "paypalcheckout",
                "method_name": "PayPal Checkout Express Test"
            }
        },
        "totals": {
            "total": 311.92,
            "discount": 0,
            "shipping": 4.9,
            "payment": 0,
            "tax": 0
        }
    },
    "meta": null,
    "error": null
}

An address of an order, with its form

GET /orders/{id}/address/{type} read since 6.6.0

The billing or shipping address of an order, together with the form the shop would use to edit it.

The form is sent with the values because a shop decides its own address fields: which exist, which are required, and in what order. Build the form from fields rather than assuming a shape, and PUT the same keys back.

Path

ParameterTypeDescription
idrequiredintegerThe order id.
typerequiredstring`billing` or `shipping`.

Response

FieldTypeDescription
typestringWhich address this is.
address_idintegerThe address row, 0 when the order has none of that kind.
fieldsobject[]The shop's address fields, in display order.
namekeystringThe key to send the value back under.
typestringWhat to render: text, zone, singledropdown, and the rest.
raw_typestringHikaShop's own name for the type.
labelstringTranslated into the operator's language.
defaultstringThe value used when none is given.
requiredbooleanWhether the shop refuses to save the address without it.
optionsobject[]The choices, for a field that has them. A country or a state is filled from the zones rather than from here.
valuestringWhat to send back when this choice is picked.
labelstringWhat to show.
label_keystringThe translation key behind the label, when there is one.
multiplebooleanWhether more than one may be chosen.
translatablebooleanNot meaningful on an address, where values are the customer's own words.
upload_dirstringUnused on an address field.
allowed_extensionsstringUnused on an address field.
date_formatstringFor a date field, the format it is stored in.
valuesobjectThe current values, keyed by field namekey. Keyed by whatever address fields this shop has.
country_namestringThe country spelled out, since the value itself is a zone id.
state_namestringThe state spelled out, empty where the country has none.

Errors

CodeHTTPMeans
not_found404No such order, or the operator may not see it.
GET/orders/{id}/address/billing
curl "$SHOP/hikashop-api/v1/orders/{id}/address/billing" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": {
        "type": "billing",
        "address_id": 19,
        "fields": [
            {
                "namekey": "address_title",
                "type": "singledropdown",
                "raw_type": "singledropdown",
                "label": "Title",
                "default": "",
                "required": true,
                "options": [
                    {
                        "value": "Mr",
                        "label": "Mr",
                        "label_key": "HIKA_TITLE_MR"
                    },
                    {
                        "value": "Mrs",
                        "label": "Mrs",
                        "label_key": "HIKA_TITLE_MRS"
                    },
                    {
                        "value": "Miss",
                        "label": "Miss",
                        "label_key": "HIKA_TITLE_MISS"
                    },
                    {
                        "value": "Ms",
                        "label": "Ms",
                        "label_key": "HIKA_TITLE_MS"
                    },
                    "… 1 more, trimmed for the example"
                ],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_firstname",
                "type": "text",
                "raw_type": "text",
                "label": "First name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_lastname",
                "type": "text",
                "raw_type": "text",
                "label": "Last name",
                "default": "",
                "required": true,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            {
                "namekey": "address_company",
                "type": "text",
                "raw_type": "text",
                "label": "Company",
                "default": "",
                "required": false,
                "options": [],
                "multiple": false,
                "translatable": false,
                "upload_dir": "",
                "allowed_extensions": "",
                "date_format": ""
            },
            "… 7 more, trimmed for the example"
        ],
        "values": {
            "address_title": "Mr",
            "address_firstname": "Alex",
            "address_lastname": "Marchand",
            "address_company": "Lilas SARL",
            "address_street": "12 rue des Lilas",
            "address_post_code": "44000",
            "address_city": "Nantes",
            "address_telephone": "+33 2 00 00 00 00",
            "address_country": "",
            "address_state": "",
            "address_vat": "FR00000000000"
        },
        "country_name": "",
        "state_name": ""
    },
    "meta": null,
    "error": null
}

The coupons that can be applied

GET /coupons read since 6.6.0

The shop's published coupons, so an operator can pick one rather than remember a code. Applying it is a separate call, and the shop validates it again there: a coupon listed here can still be refused for this particular order.

Response a list

FieldTypeDescription
idinteger
codestringWhat the customer would type. This is what you send to apply it.
flat_amountnumberA fixed reduction, 0 when the coupon is a percentage.
percent_amountnumberA percentage reduction, 0 when the coupon is a fixed amount.
currency_idintegerThe currency a flat amount is expressed in.
startinteger|nullUnix timestamp before which it is not valid.
endinteger|nullUnix timestamp after which it expires.
quotaintegerHow many times it may be used in total, 0 for no limit.
used_timesintegerHow many times it already has been.
minimum_ordernumberThe order total below which it does not apply, 0 for none.
GET/coupons
curl "$SHOP/hikashop-api/v1/coupons" \
  -H "Authorization: Bearer $TOKEN"
200success
{
    "data": [
        {
            "id": 1,
            "code": "APPTEST10",
            "flat_amount": 10,
            "percent_amount": 0,
            "currency_id": 1,
            "start": 0,
            "end": 0,
            "quota": 0,
            "used_times": 0,
            "minimum_order": 0
        },
        {
            "id": 2,
            "code": "APPTEST20PCT",
            "flat_amount": 0,
            "percent_amount": 20,
            "currency_id": 1,
            "start": 0,
            "end": 0,
            "quota": 0,
            "used_times": 0,
            "minimum_order": 0
        },
        {
            "id": 432,
            "code": "E2E1786215715618",
            "flat_amount": 0,
            "percent_amount": 12,
            "currency_id": 0,
            "start": 0,
            "end": 0,
            "quota": 0,
            "used_times": 0,
            "minimum_order": 0
        },
        {
            "id": 434,
            "code": "E2E1786215747620",
            "flat_amount": 0,
            "percent_amount": 12,
            "currency_id": 0,
            "start": 0,
            "end": 0,
            "quota": 0,
            "used_times": 0,
            "minimum_order": 0
        }
    ],
    "meta": null,
    "error": null
}

Change an order's status

POST /orders/{id}/status write since 6.6.0

Moves the order and, when asked, sends the customer the same notification the backend would have sent.

This calls the same code the backend does, so stock, invoices and every plugin listening on a status change behave exactly as they do there. It is not a database update.

Path

ParameterTypeDescription
idrequiredintegerThe order id.

Body

FieldTypeDescription
statusrequiredstringA status **namekey**, as listed by GET /statuses. Not the translated label.
notifybooleanSend the customer the notification for the new status. Defaults to false.
reasonstringRecorded in the order history, and included in the notification when there is one.

Response

FieldTypeDescription
idintegerThe order id.
statusstringThe namekey the order now has.
changedbooleanFalse when the order already had that status.
notifiedbooleanWhether the customer was actually emailed, which can be false even when you asked, if the status has no notification configured.

Errors

CodeHTTPMeans
invalid_status400No such status namekey on this shop.
not_found404No such order, or the operator may not see it.
save_failed500The order could not be saved.
POST/orders/{id}/status
curl -X POST "$SHOP/hikashop-api/v1/orders/{id}/status" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "confirmed",
    "notify": false,
    "reason": "Documentation capture"
}'
200success
{
    "data": {
        "id": 5626,
        "status": "confirmed",
        "changed": true,
        "notified": false
    },
    "meta": null,
    "error": null
}