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.
{
"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.
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.
{
"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
{
"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.
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.
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
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
hikashop-connector. A cheap way to be sure you are talking to this API and not to something else answering on that path.cmsobjectWhat it is running on.
joomla or wordpress.starter, essential or business. This API only answers on Business, so in practice it is business unless the licence has lapsed.currencyobjectThe shop's money.
operatorobjectThe user the device is bound to.
0 when the device is bound to nobody, which is a device paired without an operator.admin when they may manage the component, staff otherwise.read, and write when it was granted.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.
curl "$SHOP/hikashop-api/v1/site" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
curl "$SHOP/hikashop-api/v1/settings" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
curl "$SHOP/hikashop-api/v1/version" \ -H "Authorization: Bearer $TOKEN"
{
"data": {
"i18n": "1786385909",
"statuses": "1834471105",
"languages": "1820052638"
},
"meta": null,
"error": null
}
The shop's order statuses
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
curl "$SHOP/hikashop-api/v1/statuses" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
en-GB. Falls back to the site language when it is not installed.Response
curl "$SHOP/hikashop-api/v1/i{id}n?locale=en-GB" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
languagesobject[]The published languages.
fr-FR.fr_fr, which is what the translation tables key on.curl "$SHOP/hikashop-api/v1/languages" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Response
-1 when this product does not track stock, which is not the same as 0.GET /products/lookup matches on.weight_unit.kg, g, lb or oz.dimension_unit.dimension_unit.dimension_unit.m, cm, mm, ft or in.0 for no minimum.0 for no maximum.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.
all, none, or groups when it is restricted to some.groups.0 when the shop has none.main for a product, variant for one of its variants.0 otherwise.0 when unset.0 when the product is untaxed.0.2 is twenty percent.pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
accessobjectWho this price is for, in the same shape as the product access.
imagesobject[]In the order the editor shows them; the first is the main image.
accessobjectWho may see it.
filesobject[]Downloadable files, in the same shape as the images.
categoriesobject[]The categories the product is in.
bundleobject[]The products this one is made of, when it is a bundle.
optionsobject[]Products offered as options alongside this one.
relatedobject[]Products shown as related.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
valuesobject[]The values of it this product uses, such as S, M and L.
M.variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
null 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.
imagesobject[]The variant's own images, in the same shape as the product's.
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
custom_fields.text, radio, singledropdown, file, and the rest of HikaShop's field types.fields in the same response says what they are.curl "$SHOP/hikashop-api/v1/products/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Response
0 when the barcode belonged to the parent.Errors
curl "$SHOP/hikashop-api/v1/products/lookup?barcode=TEST{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"id": 1,
"variant_id": 0,
"name": "Test Product (Vendor 2)",
"code": "TEST001",
"gtin": "",
"quantity": -1
},
"meta": null,
"error": null
}
Products running out
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
5.20, capped at 100.Response a list
0 when the parent itself is.curl "$SHOP/hikashop-api/v1/products/low-stock?threshold={id}&limit=3" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Body
-1 turns stock tracking off for this product.Response
Errors
curl -X POST "$SHOP/hikashop-api/v1/products/2/stock" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"quantity": 42
}'
{
"data": {
"id": 2,
"quantity": 42
},
"meta": null,
"error": null
}
List customers
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
0.20, capped at 100.John Doe finds a guest who has never had an account.Response a list
registered for an account, guest for someone who ordered without one.Envelope meta
curl "$SHOP/hikashop-api/v1/customers?limit=2" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
0.20, capped at 100.discount or coupon, to list one kind.Response a list
discount applies by itself, coupon waits for its code.kind.0 for none.0 for none.0 for no limit.0 for no limit.accessobjectWhich user groups it is for, in the usual mode and groups shape.
all, none, or groups when it is restricted to some.exclude_accessobjectWhich user groups it is never for.
all, none, or groups when it is restricted to some.Envelope meta
curl "$SHOP/hikashop-api/v1/discounts?limit=2" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
0.20, capped at 100.Response a list
null when it has none.Envelope meta
curl "$SHOP/hikashop-api/v1/categories?limit=3" \ -H "Authorization: Bearer $TOKEN"
{
"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
A category with its description, its image, who may see it, and your own category fields.
Path
Response
product, manufacturer, tax, and so on.accessobjectWho may see it.
all, none, or groups when it is restricted to some.null when it has none.fields says what they are.curl "$SHOP/hikashop-api/v1/categories/2" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
product, order, user, category, address.Response a list
Errors
curl "$SHOP/hikashop-api/v1/massactions?table=product" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Response
imagesobject[]The images inside it.
Errors
curl "$SHOP/hikashop-api/v1/media/browse" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
country, state or a zone group.Response a list
FRA or US-CA.country, state, or the kind of grouping it is.curl "$SHOP/hikashop-api/v1/zones?search=fra" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Response a list
curl "$SHOP/hikashop-api/v1/users?search=a" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
currenciesobject[]Every published currency, with enough to format an amount the way the shop does.
EUR.tax_categoriesobject[]The tax categories a product can be put in.
tax_id on a product stores.characteristicsobject[]Every characteristic in the shop, with its values, for building variants.
valuesobject[]Its values.
M.kg.m.product_fieldsobject[]The definitions of your own product fields.
text, radio, singledropdown, file, and the rest.optionsobject[]The choices, for a field that has them.
category_fieldsobject[]The definitions of your own category fields.
text, radio, singledropdown, file, and the rest.optionsobject[]The choices, for a field that has them.
warehousesobject[]The warehouses stock can be held in. Empty when the shop has none.
warehouse_id on a product stores.tagsobject[]The CMS tags a product can carry.
curl "$SHOP/hikashop-api/v1/products/meta" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Body
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
accessobjectWhich user groups the price is for.
all, none or groups.Errors
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
}
]
}'
{
"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
Replaces the set of categories the product is in. As with the prices, send the complete set: what you leave out is removed.
Path
Body
Response a list
Errors
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/categories" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"categories": [
"230"
]
}'
{
"data": [
{
"id": 230,
"name": "Planners"
}
],
"meta": null,
"error": null
}
A product's translations
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
Response
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.
fr-FR.columnsobject[]What can be translated on this record: HikaShop's own texts plus every custom field flagged translatable.
product_name. This is the key to send a translation under.text for a line, textarea for prose, so a client knows which control to draw.Errors
curl "$SHOP/hikashop-api/v1/products/1/translations" \ -H "Authorization: Bearer $TOKEN"
{
"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
The same as for a product, for a category: its name, its description, its SEO texts and its translatable custom fields.
Path
Response
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.
fr-FR.columnsobject[]What can be translated on this record: HikaShop's own texts plus every custom field flagged translatable.
product_name. This is the key to send a translation under.text for a line, textarea for prose, so a client knows which control to draw.Errors
curl "$SHOP/hikashop-api/v1/categories/2/translations" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
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
Errors
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"
}
}'
{
"data": {
"id": 1,
"saved": 1
},
"meta": null,
"error": null
}
Save a category's translations
The same as for a product, for a category.
Path
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
Errors
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"
}
}'
{
"data": {
"id": 2,
"saved": 1
},
"meta": null,
"error": null
}
Dashboard figures
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
day, week, month or year. Defaults to month.Response
totalsobjectThe four headline figures.
previousobjectThe same four figures for the preceding period of the same length, for a comparison.
revenue_seriesobject[]One point per interval, for a chart.
top_productsobject[]The best sellers of the period.
curl "$SHOP/hikashop-api/v1/stats/dashboard?range=month" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
curl "$SHOP/hikashop-api/v1/groups" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Query
1.Response
Errors
curl "$SHOP/hikashop-api/v1/orders/{id}/products/precompute?product_id=1&quantity=2" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
curl "$SHOP/hikashop-api/v1/customers/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Response
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"deleted": 5272
},
"meta": null,
"error": null
}
Update a customer's profile
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
Body
GET /groups.Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Alex Marchand"
}'
{
"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
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
Body
Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
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"
}'
{
"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
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
-1 when this product does not track stock, which is not the same as 0.GET /products/lookup matches on.weight_unit.kg, g, lb or oz.dimension_unit.dimension_unit.dimension_unit.m, cm, mm, ft or in.0 for no minimum.0 for no maximum.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.
all, none, or groups when it is restricted to some.groups.0 when the shop has none.main for a product, variant for one of its variants.0 otherwise.0 when unset.0 when the product is untaxed.0.2 is twenty percent.pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
accessobjectWho this price is for, in the same shape as the product access.
imagesobject[]In the order the editor shows them; the first is the main image.
accessobjectWho may see it.
filesobject[]Downloadable files, in the same shape as the images.
categoriesobject[]The categories the product is in.
bundleobject[]The products this one is made of, when it is a bundle.
optionsobject[]Products offered as options alongside this one.
relatedobject[]Products shown as related.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
valuesobject[]The values of it this product uses, such as S, M and L.
M.variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
null 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.
imagesobject[]The variant's own images, in the same shape as the product's.
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
custom_fields.text, radio, singledropdown, file, and the rest of HikaShop's field types.fields says what they are.Errors
curl -X POST "$SHOP/hikashop-api/v1/products" \
-H "Content-Type: application/json" \
-d '{
"name": "Documentation capture product",
"code": "DOC-CAPTURE-1"
}'
{
"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
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
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
-1 when this product does not track stock, which is not the same as 0.GET /products/lookup matches on.weight_unit.kg, g, lb or oz.dimension_unit.dimension_unit.dimension_unit.m, cm, mm, ft or in.0 for no minimum.0 for no maximum.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.
all, none, or groups when it is restricted to some.groups.0 when the shop has none.main for a product, variant for one of its variants.0 otherwise.0 when unset.0 when the product is untaxed.0.2 is twenty percent.pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
accessobjectWho this price is for, in the same shape as the product access.
imagesobject[]In the order the editor shows them; the first is the main image.
accessobjectWho may see it.
filesobject[]Downloadable files, in the same shape as the images.
categoriesobject[]The categories the product is in.
bundleobject[]The products this one is made of, when it is a bundle.
optionsobject[]Products offered as options alongside this one.
relatedobject[]Products shown as related.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
valuesobject[]The values of it this product uses, such as S, M and L.
M.variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
null 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.
imagesobject[]The variant's own images, in the same shape as the product's.
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
custom_fields.text, radio, singledropdown, file, and the rest of HikaShop's field types.fields says what they are.Errors
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Desk lamp, brass"
}'
{
"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
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
Response
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/products/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"id": 9028,
"deleted": true
},
"meta": null,
"error": null
}
Reconcile the variant set
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
Body
values it stands for, and may carry code, quantity, published and price.Response
Errors
curl -X PUT "$SHOP/hikashop-api/v1/products/1/variants" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"variants": []
}'
{
"data": {
"characteristics": [],
"variants": []
},
"meta": null,
"error": null
}
Edit one variant
Changes one variant without touching the others, which is what a stock correction or a price change on a single size needs.
Path
Body
Any of code, quantity, published and price, and your own product fields. What you do not send is left alone.
Response
-1 when this product does not track stock, which is not the same as 0.GET /products/lookup matches on.weight_unit.kg, g, lb or oz.dimension_unit.dimension_unit.dimension_unit.m, cm, mm, ft or in.0 for no minimum.0 for no maximum.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.
all, none, or groups when it is restricted to some.groups.0 when the shop has none.main for a product, variant for one of its variants.0 otherwise.0 when unset.0 when the product is untaxed.0.2 is twenty percent.pricesobject[]Every price row, including the restricted ones. A product with none is not sellable.
accessobjectWho this price is for, in the same shape as the product access.
imagesobject[]In the order the editor shows them; the first is the main image.
accessobjectWho may see it.
filesobject[]Downloadable files, in the same shape as the images.
categoriesobject[]The categories the product is in.
bundleobject[]The products this one is made of, when it is a bundle.
optionsobject[]Products offered as options alongside this one.
relatedobject[]Products shown as related.
characteristicsobject[]The characteristics this product varies on. Empty when it has no variants.
valuesobject[]The values of it this product uses, such as S, M and L.
M.variantsobject[]Every variant, with its own code, stock, price and images. Empty for a product that does not vary.
null 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.
imagesobject[]The variant's own images, in the same shape as the product's.
fieldsobject[]The definitions of the custom fields that apply to this product, so a client can build a form for them.
custom_fields.text, radio, singledropdown, file, and the rest of HikaShop's field types.fields says what they are.Errors
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/variants/{id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"quantity": 7
}'
{
"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
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
Body
Response
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.
shippingobjectThe shipping charge and what carried it.
paymentobjectThe payment fee and what took it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
Errors
curl -X POST "$SHOP/hikashop-api/v1/orders/{id}/coupon" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "APPTEST10"
}'
{
"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
Takes the coupon or the hand-applied discount off the order and re-totals it. There is nothing to send.
Path
Response
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.
shippingobjectThe shipping charge and what carried it.
paymentobjectThe payment fee and what took it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/orders/{id}/coupon" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Body
1.Response
itemsobject[]The lines as they now stand, in the same shape as on the order.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
Errors
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
}'
{
"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
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
Body
0 removes the line.Response
itemsobject[]The lines as they now stand.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
Errors
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/products/{id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"quantity": 2
}'
{
"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
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
Body
Response
Errors
curl -X POST "$SHOP/hikashop-api/v1/massactions/2" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"9027"
]
}'
{
"data": {
"ok": true,
"count": 1,
"report": []
},
"meta": null,
"error": null
}
Create an order by hand
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
guest.email and usually a name.Response
Errors
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"
}
}'
{
"data": {
"id": 5632
},
"meta": null,
"error": null
}
Save an order's custom fields
Writes the merchant's own order fields. Only the fields you send are touched.
Path
Body
Response
Errors
curl -X PUT "$SHOP/hikashop-api/v1/orders/{id}/fields" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fields": []
}'
{
"data": {
"id": 5630,
"custom_fields": [],
"custom_field_files": []
},
"meta": null,
"error": null
}
Save an address of an order
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
Body
GET returned in values.Response
summaryobjectThe address ready to show, without re-reading the order.
formattedobjectLaid out the way this shop lays addresses out, which follows its address format setting.
Errors
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"
}
}'
{
"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
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
Body
path.GET /media/browse returns.Response
accessobjectWho may see or download it.
all, none, or groups when it is restricted to some.Errors
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"
}'
{
"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
Changes the name, the description or the access of something already attached, without re-uploading the bytes.
Path
Body
Any of name, description, access and, for a file, free_download. What you do not send is left alone.
Response
accessobjectWho may see or download it.
all, none, or groups when it is restricted to some.Errors
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"
}'
{
"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
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
Response
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/products/{id}/files/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"id": 8326,
"deleted": true
},
"meta": null,
"error": null
}
Reorder images and files
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
Body
Response
imagesobject[]The images as they now stand, in their new order, each in the same shape as on the product.
accessobjectWho may see or download it.
all, none, or groups when it is restricted to some.filesobject[]The files as they now stand, in the same shape as on the product.
accessobjectWho may see or download it.
all, none, or groups when it is restricted to some.Errors
curl -X PUT "$SHOP/hikashop-api/v1/products/{id}/media/order" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"images": [
7756,
7757
]
}'
{
"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
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
Response
The content of the file requested, with the content type taken from its extension.
Errors
Upload the value of a file field
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
Body
Response
Errors
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"
}'
{
"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
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
Response
shippingobjectThe shipping choice.
plugin_id. _ when nothing is set, so the current one always matches an option.optionsobject[]What it could move to.
plugin_id pairing to send when changing the method.paymentobjectThe payment choice, in the same shape.
optionsobject[]What it could move to.
plugin_id pairing to send.Errors
curl "$SHOP/hikashop-api/v1/orders/{id}/methods" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
curl -X PUT "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}/default" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
value.parent_id.Response
0 for a characteristic, the characteristic for a value.Errors
curl -X POST "$SHOP/hikashop-api/v1/products/characteristics" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Documentation capture"
}'
{
"data": {
"id": 36,
"value": "Documentation capture",
"parent_id": 0
},
"meta": null,
"error": null
}
Create a product category
Creates a category under another, or at the top of the product tree when no parent is given.
Body
Response
Errors
curl -X POST "$SHOP/hikashop-api/v1/products/categories" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Documentation capture"
}'
{
"data": {
"id": 253,
"name": "Documentation capture",
"parent_id": 2,
"published": true
},
"meta": null,
"error": null
}
Create a manufacturer
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
Response
manufacturer_id on a product stores.Errors
curl -X POST "$SHOP/hikashop-api/v1/products/manufacturers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Documentation capture brand"
}'
{
"data": {
"id": 254,
"name": "Documentation capture brand",
"parent_id": 10,
"published": true
},
"meta": null,
"error": null
}
Update a category
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
Body
Any of name, parent_id, published, description, meta_description, access and custom_fields. What you do not send keeps its value.
Response
fieldsobject[]The definitions of your own category fields.
text, radio, singledropdown, file, and the rest.optionsobject[]The choices, for a field that has them.
product, manufacturer, tax, and so on.accessobjectWho may see it.
all, none, or groups when it is restricted to some.null when it has none.fields says what they are.Errors
curl -X PUT "$SHOP/hikashop-api/v1/categories/{id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Shampoo"
}'
{
"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
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
Response
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/categories/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"id": 255,
"deleted": true
},
"meta": null,
"error": null
}
Read one discount or coupon
One reduction with every restriction it carries.
Path
Response
discount applies by itself, coupon waits for its code.kind.0 for none.0 for none.0 for no limit.0 for no limit.accessobjectWhich user groups it is for, in the usual mode and groups shape.
all, none, or groups when it is restricted to some.exclude_accessobjectWhich user groups it is never for.
all, none, or groups when it is restricted to some.Errors
curl "$SHOP/hikashop-api/v1/discounts/1" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
discount applies by itself, coupon waits for its code.kind.0 for none.0 for none.0 for no limit.0 for no limit.accessobjectWhich user groups it is for, in the usual mode and groups shape.
all, none, or groups when it is restricted to some.exclude_accessobjectWhich user groups it is never for.
all, none, or groups when it is restricted to some.Errors
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
}'
{
"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
Changes the fields you send and leaves the rest alone, and answers with the reduction as it now stands.
Path
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
discount applies by itself, coupon waits for its code.kind.0 for none.0 for none.0 for no limit.0 for no limit.accessobjectWhich user groups it is for, in the usual mode and groups shape.
all, none, or groups when it is restricted to some.exclude_accessobjectWhich user groups it is never for.
all, none, or groups when it is restricted to some.Errors
curl -X PUT "$SHOP/hikashop-api/v1/discounts/1" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "flat",
"value": 10
}'
{
"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
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
Response
Errors
curl -X DELETE "$SHOP/hikashop-api/v1/discounts/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"data": {
"deleted": 489
},
"meta": null,
"error": null
}
Create a customer
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
Response
Errors
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"
}'
{
"data": {
"id": 5275
},
"meta": null,
"error": null
}
Attach a downloadable file
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
Body
path.Response
accessobjectWho may see or download it.
all, none, or groups when it is restricted to some.Errors
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"
}'
{
"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
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
Response
0 for a new one.billing and shipping it is used for.null when it is not a default.fieldsobject[]The shop's address fields, in display order.
text, radio, singledropdown, file, and the rest.optionsobject[]The choices, for a field that has them.
Errors
curl "$SHOP/hikashop-api/v1/customers/{id}/addresses" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Response
0 for a new one.billing and shipping it is used for.null when it is not a default.fieldsobject[]The shop's address fields, in display order.
text, radio, singledropdown, file, and the rest.optionsobject[]The choices, for a field that has them.
Errors
curl "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Body
values.billing and shipping this address is for. Defaults to both.Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
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"
}
}'
{
"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
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
Body
values.billing and shipping this address is for. Defaults to both.Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
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"
}
}'
{
"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
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
Body
values.billing and shipping this address is for. Defaults to both.Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
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"
}
}'
{
"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
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
Body
values.billing and shipping this address is for. Defaults to both.Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
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"
}
}'
{
"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
Removes an address from a customer. Orders that used it keep their own copy of it.
Path
Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
Removes an address from a customer. Orders that used it keep their own copy of it.
Path
Response
0 for a guest with no account.registered or guest.groupsobject[]The groups they are in.
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.
addressesobject[]Their addresses, defaults first.
billing and shipping it is used for.formattedobjectThe address laid out the way this shop lays addresses out, which depends on its address format setting.
ordersobject[]Their orders, newest first, enough to list them.
fields says what they are.Errors
curl -X DELETE "$SHOP/hikashop-api/v1/customers/{id}/addresses/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Device.Response
read, and write when the code granted it.Errors
curl -X POST "$SHOP/hikashop-api/v1/pair" \
-H "Content-Type: application/json" \
-d '{
"device_name": "Counter tablet",
"platform": "android",
"code": "7E9A0A"
}'
{
"data": {
"token": "hk_dev_3f9c1a……",
"scopes": [
"read",
"write"
],
"device_id": 88
},
"meta": null,
"error": null
}
List products
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
0.20 and is capped at 100.Response a list
-1 when the product does not track stock, which is not the same as 0.GET /products/{id} for the variants themselves.null.null when the product has no price row at all.fields in the envelope says what they are.Envelope meta
curl "$SHOP/hikashop-api/v1/products?limit=2" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
0.20, capped at 100.GET /statuses.Response a list
GET /statuses translates it.customerobjectEnough to name the buyer in a list.
fields in the envelope says what they are.Envelope meta
custom_fields.curl "$SHOP/hikashop-api/v1/orders?limit=2" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Response
totalsobjectThe figures. See the shape below.
customerobjectWho placed it.
itemsobject[]The lines: id, name, code, quantity, price, tax and whether the line can still be edited.
PUT /orders/{id}/products/{lineId} takes. It is not the product id.null when there is none. It is a copy, not a pointer at the customer's current address.historyobject[]What has happened to the order, oldest first.
fields says what they are.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.
shippingobjectThe shipping charge and what carried it.
paymentobjectThe payment fee and what took 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.
0.1 is ten percent.curl "$SHOP/hikashop-api/v1/orders/{id}" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
Body
discount, shipping and payment, each an object with at least an amount, tax excluded. A discount is a positive figure and is subtracted.Response
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.
shippingobjectThe shipping charge and what carried it.
paymentobjectThe payment fee and what took it.
totalsobjectThe order totalled, so a client need not compute it and disagree with the shop.
Errors
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
}
}
}'
{
"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
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
Response
0 when the order has none of that kind.fieldsobject[]The shop's address fields, in display order.
text, zone, singledropdown, and the rest.optionsobject[]The choices, for a field that has them. A country or a state is filled from the zones rather than from here.
Errors
curl "$SHOP/hikashop-api/v1/orders/{id}/address/billing" \
-H "Authorization: Bearer $TOKEN"
{
"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
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
0 when the coupon is a percentage.0 when the coupon is a fixed amount.0 for no limit.0 for none.curl "$SHOP/hikashop-api/v1/coupons" \ -H "Authorization: Bearer $TOKEN"
{
"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
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
Body
GET /statuses. Not the translated label.false.Response
Errors
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"
}'
{
"data": {
"id": 5626,
"status": "confirmed",
"changed": true,
"notified": false
},
"meta": null,
"error": null
}


















