Merchi SDK logo

Getting Started Edit

Welcome to the official Merchi API documentation page. This API document is designed for those interested in developing for Merchi.

In most cases, we recomend using one of our prebuilt SDK libraries, which are discussed first. They are available for TypeScript, JavasScript, and python.

However, if you do not like them, or one is not available for your preferred programming language, you can also talk directly to our REST API, which is also documented here.

Typescript Edit

An SDK is available for typescript.

Getting Started Edit

The typescript SDK is available from github. Some dependencies are required, which can be installed with either yarn or npm at your preference.

git clone https://github.com/merchisdk/sdk.git
cd typescript
yarn
git clone https://github.com/merchisdk/sdk.git
cd typescript
npm install

The Merchi object Edit

All functionality is available via the Merchi class. Your first step when using the typescript SDK must be to create an instance of this class.

Most types of requests against the Merchi API need to be authenticated. If your code will be running in a web browser environment, the Merchi object will automatically fetch the sessiont token of the currently logged in user from the Merchi cookie. Alternatively you can provide the optional sessionToken argument to the Merchi constructor. For example, you could put something like this into a file named index.ts.

You may need to adjust the import path in the examples, if you have not placed the typescript SDK into “merchisdk/typescript/”.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const sessionToken = "rk1fGoPW7cyxa8cCLR45CigUtjkO1iCWWuLrK08CDaYD2gHoPHYtF7KsfTgmFcwl8tOyQssaIchgzbTSarjk8A";
const merchi = new Merchi(sessionToken);

Compiling Edit

For large projects, you may want to use a bundler like webpack, however for getting started, the code compiles just fine with the typescript compiler alone. The following command, for example, will create an output file named index.js, assuming you have written some code in a file named index.ts. The resulting output can be added directly to your website.

tsc index.ts
<script type="application/javascript" src="index.js"></script>

Listing Entities Edit

Merchi breaks up entities into different types, which loosely corrospond to REST resources. For each type, there is normally many specific entities available. Once you have set up the merchi object, you can get an array of entities with the list method. For example, here is how to get an array of ‘categories’ in the system (categories are groupings of products):

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
merchi.Categories.list().then((result) => {
  for const (category of result.items) {
    console.log(category.id, category.name);
  }
});

Fetching a Single Entity Edit

Almost every entity in Merchi is referenced by its id.

Once you have the id of an object, you can fetch that entity specifically using the get static method:

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const categoryId = 42;
const myCategory = merchi.Categories.get(categoryId);
  .then(() => {
    console.log(myCategory.name);
  });

Creating a New Entity Edit

New entities can be added to the system with the create method.

First create a new SDK object using the constructor given by the Merchi object, then set up some values on the new object, and then call create.

Creating and editing javascript/typescript objects locally has no effect on the server. Only after you call create will the object actually be stored with merchi via network request.

You do not need to assign an id when setting up the object, merchi will create one for the new object automatically.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const newCategory = new merchi.Categories();
newCategory.name = "canned food";
newCategory.create().then(() => {
  console.log("The new categories id is: " + newCategory.id);
});

Editing an Entity Edit

Existing entities can be edited using the save method.

First edit the attributes of the object locally, ensure that the id attribute is set, and then call save.

You can use the objects returned by list or get for editing as those objects will have their id filled in. But if you already know the id of the entity that you wish to edit, there is no need to first fetch the entity from the server before editing, you can specify the id directly.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const categoryToEdit = new merchi.Categories();
categoryToEdit.id = 42;
categoryToEdit.name = "dry food"; // make a correction to the name
categoryToEdit.save().then(() => {
  console.log("ok, the category name was edited.");
});

Deleting an Entity Edit

Entities can be deleted via the delete method.

As with editing, if you know the id of the object, you do not have to fetch it before deleting.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const categoryToDelete = new merchi.Categories();
categoryToDelete.id = 42;
categoryToDelete.delete().then(() => {
  console.log("ok, the category with id 42 was deleted.");
});

Creating Nested entities Edit

Entities can have relationships to other entities.

For example, categories are always linked to a domain, which is the Merchi name for a store.

When creating an entity, you can attach a Merchi Domain object to the category object, in order to specify when domain it should be attached to.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const newCategory = new merchi.Categories();
const existingDomain = new merchi.Domains();
existingDomain.id = 42;  // note: this id already exists
newCategory.domain = existingDomain;
newCategory.name = "vegetables";
newCategory.create().then(() => {
  console.log("ok, new vegetables category created on domain 42");
});

Embedding Edit

Because entities can be nested arbitrarily deeply (and indeed, cyclic references are possible), the get and list methods do not, by default, fetch nested entities from the server. Only scalar properties (strings, numbers, dates, …) are included.

It is possible however to request that the server also include specific nested entities, to a specific depth using the embed parameter, as in the following example.

On a newly fetched category category.domain will be undefined, even if the category has a domain on the server. undefined means that the domain has not been included, or updated locally. If the category did not have a domain at all, category.domain would instead be null. ‘null and undefined` thus have seperate meanins in the merchi sdk library.

The following example shows only get, but the embed parameter can be used with both get and list methods.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const embed = {'domain': {'theme': {}}};
merchi.Categories.get(42, {embed: embed}).then((category) =>
   const themeId = category.domain.theme.id;
   console.log('the id of the theme of the domain of category 42 is: ' + themeId);
});

Nested Editing Edit

Calling save on an entity will also automatically save any local changes to attached nested entities.

In this example, we edit the name of a store “through” the category object:

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
const category = new merchi.Categories();
category.id = 12;   // assume we already have the entity id's
const domain = new merchi.Domains();
domain.id = 42;
category.domain = domain;
domain.domain = "new-store.example.com";  // newly chosen name
category.save().then(() => {
   console.log("ok, the category and domain are both updated.");
});

Types vs Constructors Edit

To work around a small limitation in typescript, the entitiy types are seperated from their constructors. If you want to list or create entities, it must always be done through the constructor attached to the Merchi object. The types, however, are imported from the ‘entities’ directory. The following example demonstrates:

Never try to directly instantiate the imported types, always go through the Merchi object.

import { Merchi } from 'merchisdk/typescript/src/merchi';
import { Category } from 'merchisdk/typescript/src/entities/category';
const merchi = new Merchi();
const category: Category = new merchi.Category();

Pagination Edit

As there may be tens or hundreds of thousands of some types of entities, list will not return them all at once, but rather it will return one “page” of results.

The limit option controls how many results will be returned per page (further limited by backend-enforced limits, and how many entities are actually available). It defaults to 25 if not specified.

The offset option controls how many entities to skip over. For example, if the limit is set to 10, setting offset to 20 will give you the third page. It defaults to zero if not not specified.

Additionally, the ListMetadata type is available on the response of a call to list, which contains information about how many entities where returned to this query, and how many are available in total (if the limit were infinite and the offset were zero).

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
merchi.Categories.list({offset: 10,
                        limit: 10).then((result) => {
  const categories = result.items;
  const info = result.metadata;
  console.log("Categories returned: " + info.count);
  console.assert(categories.length === info.count, "oh no!");
  console.log("Categories available: " + info.available);
});

Searching Edit

Most entities also accept a q parameter for searching or filtering by keywords.

The following example shows how to find products related to eggs (the product might have the word egg in it’s name, or description, or might be in a category named egg, or so on).

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
merchi.Products.list({q: "egg").then((result) => {
  for (const product of result.items) {
      console.log(product.name);
  }
});

Filtering Edit

In addition to straight search, many more specific filtering parameters are available. For example the inDomain filter restricts list results to only those from a specific domain, and the tags filter restricts list results to only those with a specific Tag added. The exact list of supported filters varies by entity. For a complete list, see the Filtering Reference.

import { Merchi } from 'merchisdk/typescript/src/merchi';
const merchi = new Merchi();
merchi.Products.list({tags: ["big", "blue"]).then((result) => {
  for (const product of result.items) {
      console.log(product.name);
  }
});

Error Handling Edit

As the typescript API uses javascript Promises, errors can be caught using promise.catch()

The error object received will be an instance of ApiError;

import { Merchi } from 'merchisdk/typescript/src/merchi';
import { ApiError } from 'merchisdk/typescript/src/request';
const merchi = new Merchi();
merchi.Categories.get(42)
  .then(() => {
    console.log("ok, got a category");
  })
  .catch((err: ApiError) => {
    console.error(`error: ${err.errorCode} ${err.statusCode} ${err.errorMessage}`)
  });
});

Other Entities Edit

The examples so far have mostly used products and categories. You may be wondering what other entities exist. A complete list is available in the API reference.

JavaScript Edit

In most cases, we recomend you use our Typescript SDK instead. It is newer, uses newer, more convenient programming techniques, and, of course, being typescript, allows for static typechecking.

Nonetheless, a straight JavaScript version also exists, and is still maintained. One advantage of the JavaScript library is that it will work directly in browsers with no need to setup a compiler toolchain.

Getting Started Edit

The latest version of the javascript SDK is available, minified at:

https://merchi.co/static/js/dist/merchi.js

You can download it and include it in your website or other project, but you are also welcome to hotlink it directly.

Unminified source code is also available here:

https://github.com/merchisdk/sdk/tree/master/javascript
<script type="javascript" src="https://merchi.co/static/js/dist/merchi.js">
</script>

The Merchi object Edit

All functionality is contained in a Merchi object which must manually be initialised. This helps with namespacing. The remainder of the examples in this document will assume that you have already set up a merchi object as follows:

const merchi = SDK.merchi("https://api.merchi.co/", "https://websockets.merchi.co/");

Fluent API Edit

Entities in the javascript SDK (unlike the typescript SKD) use a fluent style API.

That means that to get the value of an attribute, you must call a method with that name, giving no arguments, and it will return the value.

To set the value, you must call the same method, but providing the new value. When setting a value, the original object will be returned, which lets you ‘chain’ attribute setting calls.

// getting and setting
const domain = new merchi.Domain()
console.assert(domain.domain() === undefined, "attribute not yet set");
const name = "new-value.example.com";
const domain2 = domain.domain(name);
console.assert(domain2 === domain, "the original domain is returned");
console.assert(domain.domain() === name, "attribute has now been set");

// example of chaining multiple sets and a single get into one line
const logoUrl = domain.domain("even-newer-value.example.com")
      .smsName("sms.example.com")
      .enableSmsNotifications(true)
      .enableEmailNotifications(false)
      .logoUrl()
console.assert(domain.enableSmsNotifications(), "was set to true");
console.assert(!domain.enableEmailNotifications(), "was set to false");

Listing Entities Edit

A list of entities can be retrieved by taking the entity name in mixedCase and calling the get function on it.

Note that callbacks must be provided to get the result.

function onSuccess(products) {
    console.log("Ok, got some products!");
    for (const product of products) {
        console.log(product.id());
    }
}
const onError = console.error.bind(console);
const parameters = {};
merchi.products.get(onSuccess, onError, parameters); 

Fetching a single Entity Edit

The API for fetching a single entity is different to that in the typescript API. You must first create an entity object locally, and then set the id value, and call get on it.

Success and failure callbacks should be provided in order to know when the operation is complete, but it is the original provided entity that gets “filled in” with data from the server.

const product = new merchi.Product()
product.id(42);   // we want the product with id 42.
function onSuccess() {
    // at this point, `product` has been updated from the Merchi server.
    console.log(`the product name is: ${product.name()}`);
}
const onError = console.error.bind(console);
product.get(onSuccess, onError);  // this call makes the network request

Creating a new entity Edit

As with fetching, to create a new entity on the server, you must first create a new entity object locally in javascript. Instead of filling in the objects id, you should fill in the data that you want to provide to the new object, and then call create. Like the other method in the javascript SDK, create takes onSuccess and onError callbacks to let you know when the operation has completed.

const newProduct = new merchi.Product()
newProduct.name("Beans");  // we want to create a new prodcut for beans
newProduct.description("beans are a delicious legume");
function onSuccess() {
    // at this point, the new product exists on the merchi server
    console.log(`the product has been created with id ${newProduct.id()}`);
}
const onError = console.error.bind(console);
newProduct.create(onSuccess, onError);  // this call makes the network request

Editing an Entity Edit

The method for editing values on the server follows the same pattern as the others. It is named patch after the HTTP method that it uses. First, a javascript object should be created locally. Then, both the id primary key, and the attributes that you want to edit should be set, and then you may call patch. Like the others, patch accepts callbacks to let you know when the operation has completed.

const existingProduct = new merchi.Product()
existingProduct.id(42);  // we want to edit the product with id 42
existingProduct.name("lentils");  // we want to change its name to lentils
function onSuccess() {
    console.log("the product has been edited.");
}
const onError = console.error.bind(console);
existingProduct.patch(onSuccess, onError);  // this call makes the network request

Deleting an Entity Edit

To remove an entity from the server, you must create a javascript object locally, set it’s id attribute, and then call the destroy method.

const existingProduct = new merchi.Product()
existingProduct.id(42);  // we want to delete the product with id 42
function onSuccess() {
    console.log("the product is gone");
}
const onError = console.error.bind(console);
existingProduct.destroy(onSuccess, onError);  // this call makes the network request

Creating Nested Entities Edit

As with the typescript SDK, nested entities can be created at once. For example, you might create a product and its category in the same request.

const newProduct = new merchi.Product();
newProduct.name("lime");
newProduct.description("limes are green");
const newCategory = new merchi.Category();
newCategory.name("fruits");
// note that an array is provided as products may be in
// multiple categories.
newProduct.categories([newCategory]);
function onSuccess() {
    console.log("the product and category both now exist");
}
const onError = console.error.bind(console);
newProduct.create(onSuccess, onError);  // this call makes the network request

Pagination Edit

As there may be tens or hundreds of thousands of some types of entities, listing them will not return them all at once, but rather it will return one “page” of results.

The limit option controls how many results will be returned per page (further limited by backend-enforced limits, and how many entities are actually available). It defaults to 25 if not specified.

The offset option controls how many entities to skip over. For example, if the limit is set to 10, setting offset to 20 will give you the third page. It defaults to zero not not supplied.

Attached to the result array sent to the success callback is also a meta object containing information about how many results in total are available.

function onSuccess(products) {
    console.log(`Got ${products.length} products`);
    console.log(`${products.meta.available} products are available`);
    console.log(products);
    console.assert(products.length == products.meta.count);
    console.assert(products.meta.limit == 2);
    console.assert(products.meta.offset == 3);
}
const onError = console.error.bind(console);
merchi.products.get(onScuccess, onError, {offset: 3,
                                          limit: 2});

Searching Edit

Most entities also accept a q parameter in their list method, which allows you to filter by search terms. The following example shows how to search for products related to eggs (the returned products might have the word egg in their names, or descriptions, or might be in a category named egg, or so on).

function onSuccess(products) {
    for (const product of products) {
      console.log(product.name());
    }
}
const onError = console.error.bind(console);
merchi.products.get(onSuccess, onError, {q: "egg"});

Embedding Edit

Because entities can be nested arbitrarily deeply (and indeed, cyclic references are possible), the fetching and listing methods do not, by default, fetch nested entities from the server. Only scalar properties (strings, numbers, dates, …) are included.

It is possible however to request that the server also include specific nested entities, to a specific depth using the embed parameter, as in the following example.

On a newly fetched category category.domain() will be undefined, even if the category has a domain on the server. undefined means that the domain has not been included, or updated locally. If the category did not have a domain at all, category.domain() would instead be null. ‘null and undefined` thus have seperate meanins in the merchi sdk library.

The following example shows only fetching a single entity, but the embed parameter can also be used when listing entities.

const category = new merchi.Category()
category.id(42);
function onSuccess() {
    const themeId = category.domain().theme().id(); 
    console.log('the id of the theme of the domain of category 42 is: ' + themeId);
}
const onError = console.error.bind(console);
const embed = {'domain': {'theme': {}}};
merchi.categories.get(onSuccess, onError, {embed: embed});

Filtering Edit

In addition to straight search, many more specific filtering parameters are available. For example the inDomain filter restricts list results to only those from a specific domain, and the tags filter restricts list results to only those with a specific Tag added. The exact list of supported filters varies by entity. For a complete list, see the Filtering Reference.

function onSuccess(products) {
    for (const product of products) {
      console.log(product.name());
    }
}
const onError = console.error.bind(console);
merchi.products.get(onSuccess, onError, {tags: "big,blue"});

Completeness Edit

The typescript SDK is used internally more heavily than the javascript and PHP SDK’s. It may consequently also be kept more up to date. The other SDK’s are nonetheless fully suppported. If you happen to come across any missing feature, please contact us and we should be able to add it reasonably quickly.


Python Edit

An SDK is available for python.

Getting Started Edit

The python SDK is available from github. Only python3 is supported. Some dependencies are required, which can be installed with pip.

git clone https://github.com/merchisdk/sdk.git
cd python
python3 -m venv venv
source venv/bin/activate
pip install - r requirements/dev.txt 

Configuration Edit

The python SDK requires some configuration before use.

The rest of this documentation assumes that you have already configured the SDK according to the instructions in this section.

The request class must be subclassed. In the subclass, set up the server and host values. You can optionally also provide a session_token cookie, if the request should be authenticated as a specific user. The entity class’s request class must then be set to the new subclass. The following example demonstrates:

import sdk.python.request
import sdk.python.entities


class ApiRequest(sdk.python.request.Request):
    def __init__(self, forbid_auto_update=False):
        super(ApiRequest, self).__init__()
        self.server = "https://api.merchi.co/"
        self.host = "api.merchi.co"
        self.cookies['session_token'] = flask.request.cookies.get('session_token')

sdk.python.entities.Entity.request_class = ApiRequest

Listing Entities Edit

Merchi breaks up entities into different types, which loosely corrospond to REST resources. For each type, there is normally many specific entities available. For example, here is how to get an array of product categories from the server:

import sdk.python.categories
categories, _ = sdk.python.categories.categories.fetch()
print(categories)

Fetching a Single Entity Edit

Almost every entity in merchi is references by its id. Once you have the id of an object, you can fetch that entity specifically using the fetch static method.

Note the differences from listing above. Listing uses the plural, snake_cased entity name and calls fetch with no arguments, where as fetching takes the CamelCased singular entity name and calls fetch on it with the id as its argument.

from sdk.python.categories import Category
category = Category.fetch(42)
print(category)

Creating a New Entity Edit

New entities can be added to the system with the create method.

First create a new SDK object for the entity, then set up some values on the new object, and then call create.

Creating and editing objects locally has no effect on the server. Only after you call create will the object actually be stored with merchi.

You do not need to assign an id when setting up the object, merchi will create one for the new object automatically.

from sdk.python.categories import Category
new_category = Category()
new_category.name = "canned food"
new_category.create()
print(f"The new category id is: {category.id}")

Deleting an Entity Edit

Entities can be deleted via the destroy method.

If you know the id of the entity you do not have to fetch it before deleting.

from sdk.python.categories import Category
old_category = Category()
old_category.id = 42
old_category.destroy()
print(f"Ok, the category was deleted")

Editing an Entity Edit

Existing entities can be edited using the update method.

First edit the attributes of the object locally, then call update

You cna use the objects returned by fetch for editing, but if you already know the id of the entity that you wish to edit, there is no need to first fetch the entity from the server before editing; you can specify the id directly.

from sdk.python.categories import Category
existing_category = Category()
existing_category.id = 42
existing_category.name = "dry food"  # make a correction to the name
existing_category.update()
print("Ok, the category name was edited")

Creating Nested Entities Edit

Entities can have relationships to other entities.

For example, categories are always linked to a domain, which is the merchi name for a store.

When creating an entity you can attach a merchi Domain object to the category object, on order to specify which domain it should be attached to.

from sdk.python.categories import Category
from sdk.python.domains import Domain
new_category = Category()
existing_domain = Domain()
existing_domain.id = 42  # note: this id already exists
new_category.domain = existing_domain
new_category.name = "vegetables"
new_category.create()
print("Ok, new vegetables category created on domain 42")

Embedding Edit

Because entities can be nested arbitrarily deeply fetch does not, by default, fetch nested entities from the server. Only scalar properties (strings, numbers, dates, …) are included.

It is possible however to request that the server also include specific nested entities using the embed parameter, as in the following example.

On a newly fetched (or locally created) Category, category.domain will be None, even if the category has a domain on the server. You must request that the domain be embedded in order to know whether the value is truly null, or just locally unavailable.

The following example shows using embed only with fetching a single entity, but you can also supply the embed parameter when listing multiple entities.

from sdk.python.categories import Category
embed = {'domain': {'theme': {}}}
category = Category.fetch(42, embed=embed)
theme_id = category.domain.theme.id
print(f"The id of the theme of the domain of category 42 is: {theme_id}")

Nested editing Edit

Calling update on an entity will also automatically save any local changes to attached nested entities.

In this example, we edit the name of a domain “through” the category object.

from sdk.python.categories import Category
from sdk.python.domains import Domain
category = Category()
category.id = 12  # assume that we already have the entities ids
domain = Domain() 
domain.id = 42
category.domain = domain
domain.domain = "new-store.example.com"  # newly chosen name
category.update()
print("Ok, the category and domain are both updated.")

Pagination Edit

As there may be tens or hundreds of thousands of some types of entities, listing them will not return them all at once, but rather it will return one “page” of results.

The limit option controls how many results will be returned per page (further limited by backend-enforced limits, and how many entities are actually available). It defaults to 25 if not specified.

The offset option controls how many entities to skip over. For example, if the limit is set to 10, setting offset to 20 will give you the third page. It defaults to zero if not specified.

Additionally the PageSpecification is available as the second return value from a listing call to fetch, which contains information about how many entities where returned to this query, and how many are available in total (if the limit where infinite and the offset where zero).

import sdk.python.categories
query = {"limit": 10,
         "offset": 10}
categories, info = sdk.python.categories.categories.fetch(query=query)
print(f"Categories returned: {info.count}")
assert len(categories) == info.count
print(f"Categories available: {info.available}")

Searching Edit

Most entities also accept a q parameter for searching or filtering by keywords.

The following example shows how to find products related to eggs (the product might have the word egg in it’s name, or description, or might be in a category named egg, or so on).

import sdk.python.categories
query = {"q": "egg"}
categories, _ = sdk.python.categories.categories.fetch(query=query)
for category in categories:
    print(category.name)

Filtering Edit

In addition to straight search, many more specific filtering parameters are available. For example the in_domain filter restricts list results to only those from a specific domain, and the tags filter restricts list results to only those with a specific Tag added. The exact list of supported filters varies by entity. For a complete list, see the Filtering Reference.

import sdk.python.products
query = {"tags": '["big", "blue"]'}
products, _ = sdk.python.product.products.fetch(query=query)
for product in products:
    print(product.name)

Error Handling Edit

The python SDK raises python.util.api_error.ApiError in case of failure.

import sdk.python.products
from sdk.python.util.api_error import ApiError
try:
    products, _ = sdk.python.product.products.fetch()
except ApiError as e:
    print(e.message)
    print(e.status_code)
    print(e.error_code)
else:
    for product in products:
        print(product.name)

Other Entities Edit

The examples so far have mostly used categories. You may be wondering what other entities exist. A complete list is available in the API reference.

PHP Edit

An SDK is available for PHP

Getting Started Edit

The PHP SDK is available from github.

git clone https://github.com/merchisdk/sdk.git
cd php

Making an Order Edit

The following example shows how to make a web form using the PHP SDK which accepts information from a user about quantity, shipping address, and so on, and which when places an order for a product with Merchi.

The example code requires ‘products.php’ and ‘order_helper.php’, both of which are distributed with the SDK. You should change the value of $domain to reflect the id of the Merchi store that the order should be placed with.

<?php
require_once 'products.php';
require_once 'order_helper.php';

$domain = 1; // this should be set to the sales site id
$sitename = "Example Merchi Order Form";

$validates = true;
$resultMsg  = '';

$name = '';
$nameErr = '';

$emailAddress = '';
$emailAddressErr = '';

$phoneCode = 'AU';
$phoneCodeErr = '';

$phoneNumber = '';
$phoneNumberErr = '';

$lineOne = '';
$lineOneErr = '';

$lineTwo = '';
$lineTwoErr = '';

$city = '';
$cityErr = '';

$state = '';
$stateErr = '';

$country = '';
$countryErr = '';

$postcode = '';
$postcodeErr = '';

$product = '';
$productErr = '';

$quantity = '';
$quantityErr = '';

$notes = '';
$notesErr = '';

$company = '';
$companyErr = '';

$product_list = get_products_for_domain($domain);

function generate_notes()
{
    global $sitename, $product_list, $product, $notes, $quantity;
    $result = "order from: $sitename\n";
    foreach ($product_list as $a_product) {
        if ($a_product->id == $product) {
            $result .= "product: $a_product->name\n";
            break;
        }
    }
    $result .= "size: example data only\n";
    $result .= "colour: example data only\n";
    $result .= "ordered qty: $quantity\n";
    $result .= "client notes follow: \n$notes\n";
    return $result;
}

function transpose($file_post)
{
    $file_ary = [];
    $file_count = count($file_post['name']);
    $file_keys = array_keys($file_post);

    for ($i = 0; $i < $file_count; $i++) {
        foreach ($file_keys as $key) {
            $file_ary[$i][$key] = $file_post[$key][$i];
        }
    }
    return $file_ary;
}

function text_input($label, $id, $value, $err)
{
    echo "<div>";
    echo "<label for=\"$id\">$label:</label>";
    echo "<input type=\"text\" id=\"$id\" name=\"$id\"";
    $value = htmlspecialchars($value);
    echo "  value=\"$value\">";
    echo $err;
    echo "</div>";
}

function product_select()
{
    global $product_list, $product;
    echo "<div><label for=\"product\">product:</label>";
    echo "<select name=\"product\">";
    foreach ($product_list as $a_product) {
        $name = htmlspecialchars($a_product->name);
        echo "<option value=\"$a_product->id\"";
        if ($a_product->id == $product) {
            echo ' selected';
        }
        echo ">$name</option>";
    }
    echo "</select></div>";
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $name = $_POST["name"];
    if (empty($name)) {
        $nameErr = "name is required";
        $validates = false;
    }
    $emailAddress = $_POST["emailAddress"];
    if (empty($emailAddress)) {
        $emailAddressErr = "email address is required";
        $validates = false;
    }
    $phoneNumber = $_POST["phoneNumber"];
    if (empty($phoneNumber)) {
        $phoneNumberErr = "phone number is required";
        $validates = false;
    }
    $lineOne = $_POST["lineOne"];
    if (empty($lineOne)) {
        $lineOneErr = "line one is required";
        $validates = false;
    }
    $lineTwo = $_POST["lineTwo"];
    $city = $_POST["city"];
    if (empty($city)) {
        $cityErr = "city is required";
        $validates = false;
    }
    $state = $_POST["state"];
    $country = $_POST["country"];
    if (empty($country)) {
        $countryErr = "country is required";
        $validates = false;
    }
    $postcode = $_POST["postcode"];
    $product = $_POST["product"];
    if (empty($product)) {
        $productErr = "product is required";
        $validates = false;
    }
    $quantity = $_POST["quantity"];
    if (empty($quantity)) {
        $quantityErr = "quantity is required.";
        $validates = false;
    }
    if (!is_numeric($quantity) || $quantity < 1) {
        $quantityErr = "please enter a valid quantity";
        $validates = false;
    }
    $quantity = intval($quantity);
    $phoneCode = $_POST["phoneCode"];
    if (empty($phoneCode)) {
        $phoneCodeErr = "phone area code is required";
        $validates = false;
    }
    $notes = $_POST["notes"];
    $company = $_POST["company"];

    if ($validates) {
        try {
            place_order($lineOne, $lineTwo, $city, $state, $country,
                        $postcode, $emailAddress, $phoneCode, $phoneNumber,
                        $name, $company, $product, $quantity, $domain,
                        generate_notes(), transpose($_FILES['files']));
            $resultMsg = 'order has been placed!';
        } catch (Exception $e) {
            $msg = htmlspecialchars($e->getMessage());
            $resultMsg = 'error from server: ' . $msg;
        }
    } else {
        $resultMsg = 'validation failed';
    }
}

?>

<html>
  <head>
    <title>
      Example Merchi Order Form
    </title>
    <style>
      div { margin: 10px; }
    </style>
</head>
</body>
  <div>
    <?php echo $resultMsg ?>
  </div>
  <div>
    <h1>Example Merchi Order Form</h1>
  </div>
  <form method="post"
        action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"
        enctype="multipart/form-data">
    <div>
      <?php text_input("name", "name", $name, $nameErr) ?>
        <?php text_input("email address", "emailAddress", $emailAddress,
                         $emailAddressErr) ?>
        <?php text_input("area code", "phoneCode", $phoneCode,
                         $phoneCodeErr) ?>
        <?php text_input("phone number", "phoneNumber", $phoneNumber,
                         $phoneNumberErr) ?>
        <?php text_input("line one", "lineOne", $lineOne,
                         $lineOneErr) ?>
        <?php text_input("line two", "lineTwo", $lineTwo,
                         $lineTwoErr) ?>
        <?php text_input("city", "city", $city, $cityErr) ?>
        <?php text_input("state", "state", $state, $stateErr) ?>
        <?php text_input("country", "country", $country, $countryErr) ?>
        <?php text_input("postcode", "postcode", $postcode,
                         $postcodeErr) ?>
        <?php product_select() ?>
        <?php text_input("quantity", "quantity", $quantity,
                         $quantityErr) ?>
        <?php text_input("notes", "notes", $notes, $notesErr) ?>
        <?php text_input("company", "company", $company, $companyErr) ?>
        <label>Upload logo or design files:</label><br />
        <div id="file-container">
        </div>
        <a href="#" id="file-add">Upload another</a>
      </div>
      <div>
        <input type="submit" value="place order">
      </div>
    </form>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js">
    </script>
    <script>
      function add() {
        $('#file-container').append('<input name="files[]" type="file"><br />');
      }
      $('#file-add').on('click', function (e) {
        add();
        e.preventDefault();
      });
      add();
    </script>
  </body>
</html>

Filter Reference Edit

This section documents the list of filters available for reducing or searching list results. For examples of how to actually use the filters, check the individual SDK documentation for your programming language.

Note that the Merchi backend will accept filter names either in mixedCase or underscore_case, but they are only listed here in mixedCase. Typically you would use whichever one is more idiomatic in your programming language, for example, the TypeScript SDK request object has property names for each filter in mixedCase.

as Edit

The as filter should be given a user id.

It restricts the result set to only those entities that the specified user would have permission to access (as well as the current user also having access).

tab Edit

The tab filter should be given a string representing the tab name.

It restricts the result set to entities which would make sense displayed in a seperate UI tab.

The following tab names are available on Notification entities:

  • read
  • unread

The following tab names are available on Invoice entities:

  • all (does nothing)
  • overdue
  • unpaid

The following tab names are available on Job entities:

  • all (does nothing)
  • The name of any Category entity

The following tab names are available on Shipment entities:

  • receiving (the user is the receiver)
  • sending (the user is the sender)

The following tab names are available on `Usera entities:

  • super (only super user admins)

The following tab names are available on Theme entities:

  • public (only publically accessable themes)

state Edit

The state filter should be given a string representing the state name.

It restricts the result set to entities which are in a specific state. The exact meaning depends on the entity being listed.

The following states are available on the Quote entity:

  • canAddToInvoice

The following states are available on the Assignment entity:

  • assignmentsNeedShipping
  • assignmentsAll (does nothing)

The following states are available on the Job entity:

  • jobsActive
  • jobsAll (does nothing)
  • jobsAllActive
  • jobsAllActiveDrafting
  • jobsArchived
  • jobsCompleted
  • jobsUnpaid
  • jobsAllWithoutInvoices
  • jobsUnassignedProduction
  • jobsNeedShipping
  • jobsAllShipments
  • jobsShipments
  • jobsDraftingActiveDesigner
  • jobsDraftingUnassignedDesigner
  • jobsDraftingDraftsWaitingDesigner
  • jobsDraftingDraftsChangesRequestedDesigner
  • jobsProductionAllActiveSupplier
  • jobsProductionShipmentReady
  • jobsProductionShipmentDispatched
  • jobsProductionQuotingSupplier
  • jobsProductionCompleteSupplier
  • jobsActiveProductionSupplier
  • jobsProductionShipmentsSupplier
  • jobsProductionQuoteSuccessfulSupplier

The following states are available on the Shipment entity:

  • notInvoiced
  • notQuoted
  • notQuotedOrInvoice

section Edit

The section filter should be given a number representing the section id.

It is available on Notification and User entities.

senderRole Edit

The senderRole filter should be given either the string system, or, anything else. It is available only on Notification entities. system means notifications that come form the system itself, rather than another user.

inDomain Edit

The inDomain filter should be given a domain id.

It is available on most entity types, and restricts the result set to only those entities belonging to the given domain (store).

inDomainName Edit

The inDomainName filter should be given a domain name.

It is available on most entity types, and restricts the result set to only those entities belonging to the given domain (store).

It is identical to inDomain, except that it operates by name rather than by id. inDomain and inDomainName are exclusive.

inDomainRoles Edit

The inDomainRoles filter should be given a (JSON syntax) list of user role types (see the role enum in the entity reference for a list).

It is only available on User entities, and only if either inDomain or inDomainName has also been specified, in which case the users in the result set are restricted to those which have at least one of the specified roles/permissions in the specified domain.

publicOnly Edit

The publicOnly filter should be given a (JSON syntax) boolean.

If true, the result set is restricted to only those entities which are shown publically.

It is available on Category, Product, SubscriptionPlan and DomainTag entities.

isPrivate Edit

The isPrivate filter should be given a (JSON syntax) boolean.

If true, the result set is restricted to only those entities which are marked private.

It is available only on SubscriptionPlan entities. entities.

managedOnly Edit

The managedOnly filter should be given any string, to enable it.

If enabled, the result set is restricted to only those products which the user is a manager of.

It is available only on Product entities. entities.

clientOnly Edit

The clientOnly filter should be given any string, to enable it.

If enabled, the result set is restricted to only those jobs which the user is a client of.

It is available only on Job entities. entities.

teamOnly Edit

The trueOnly filter should be given the string “true”, to enable it.

If enabled, the result set is restricted to only those entities where the user is a team memember (manager, etc.) of the domain of the entity.

It is available on Job and Domain entities. entities.

managedDomainsOnly Edit

The managedDomainsOnly filter should be given any string, to enable it.

If enabled, the result set is restricted to only those entities where the user is an admin or manager of the domain of the entity.

It is available on Category, User, Theme and Domain entities. entities.

businessDomainsOnly Edit

The businessDomainsOnly filter should be given any string, to enable it.

If enabled, the result set is restricted to only those domains that user is a team member (manager, etc.) of.

It is available only on Domain entities. entities.

relatedAssignment Edit

The relatedAssignment filter should be given an Assignment id.

It restricts the result set to only those ProductionComments that are attached to the specified Assignment.

It is available only on ProductonComment entities. entities.

relatedJob Edit

The relatedJob filter should be given an Job id.

It restricts the result set to only those entities which are attached to the specified job,

It is available Notification, JobComment, and User entities. entities.

relatedProduct Edit

The relatedProduct filter should be given a Product id.

It restricts the result set to only those entities which are attached to the specified product.

It is available on ShipmentMethod, DiscountGroup, and DomainTag entities.

jobNotifiable Edit

The jobNotifiable filter should be given a Job id.

It restricts the result set to only those Users which should be notified about changes to the specified job.

It is available on only the User entity.

notificationType Edit

The notificationType filter should be given an integer representing the notification type (for a complete list, see the enum definition in the entity reference).

It restricts the result set to only those Notifications which are of the specified type.

It is available on only the Notification entity.

notificationJob Edit

The notificationJob filter should be given an integer representing a Job id.

It restricts the result set to only those Notifications which are related to the specified job.

It is available on only the Notification entity.

notificationRecipient Edit

The notificationRecipient filter should be given an integer representing a User id.

It restricts the result set to only those Notifications which are directed to the specified User.

It is available on only the Notification entity.

isOrder Edit

The isOrder filter should be given a (JSON syntax) boolean.

It is available on only the Jobs entity.

clientId Edit

The clientId filter should be given an integer representing a User id.

It restricts the result set to only those entities which have the specified user as a client.

It is available on the Job, Assignment, Quote, and Invoice entities.

managerId Edit

The managerId filter should be given an integer representing a User id.

It restricts the result set to only those entities which have the specified user as a manager.

It is available only on the Invoice entity.

clientCompanyId Edit

The clientCompanyId filter should be given an integer representing a Company id.

It restricts the result set to only those entities which have the specified Company as a client.

It is available on the Shipment, Job, Quote, and Invoice entities.

senderCompanyId Edit

The senderCompanyId filter should be given an integer representing a Company id.

It restricts the result set to only those entities which have the specified Company as the sender.

It is available on the Shipment and Assignment entities.

relatedUser Edit

The relatedUser filter should be given an integer representing a User id.

It restricts the result set to only those entities which are related specified User. The meaning of “related” is worked out in some sensible way for each type of entity. For example, a User might be related to a Job if they where either the manager, designer, or supplier for it.

It is available on the File, EnrolledDomain, Category, Product, Job, and Company entities.

dateFrom Edit

The dateFrom filter should be given a unix timestamp (though, it will attempt to guess the meaning of other timestamp formats, too)

It restricts the result set to only those entities which occur after the specified timestamp. Usually, the creation date of the entity is meant.

It is available on the Invoice and Job entities.

dateTo Edit

The dateTo filter should be given a unix timestamp (though, it will attempt to guess the meaning of other timestamp formats, too)

It restricts the result set to only those entities which occur before the specified timestamp. Usually, the creation date of the entity is meant.

It is available on the Invoice and Job entities.

categoryId Edit

The categoryId filter should be given an integer representing a Category id.

It restricts the result set to only those products which are in the specified category.

It is available only on the Product entity.

platformCategoryId Edit

The platformCategoryId filter should be given an integer representing a Category id.

It restricts the result set to only those products which are in the specified platform category.

It is available only on the Product entity.

companyId Edit

The companyId filter should be given an integer representing a Company id.

It restricts the result set to only those products which are in the specified company.

It is available on the User, SubscriptionPlan, Domain, Shipment, Job, ShipmentMethod, Invoice and CountryTax entities.

componentId Edit

The componentId filter should be given an integer representing a Component id.

It restricts the result set to only those ComponentTags which are attached to the specified Component`

It is available only on the ComponentTag entity.

name Edit

The name filter should be given a string.

It restricts the result set to only those entities whose name is as specified.

It is available on the Domain and Component entities.

tags Edit

The tags filter should be given a comma-seperated list of integers, each representing a tag id.

It restricts the result set to only those entities tagged with the specified tags.

It is available on the Product, Job, and Components entities.

tagNames Edit

The tagNames filter should be given a comma-seperated list of strings representing a tag name.

It restricts the result set to only those entities tagged with the specified tags (by name).

It is available only on the Component entity.

exclude Edit

The exclude filter should be given a comma-seperated list of integers, each representing an entity id.

It restricts the result set to only those entities not indicated in the list.

It is available on all entities.

excludeDomains Edit

The excludeDomains filter should be given a comma-seperated list of integers, each representing a Domain id.

It restricts the result set to only those products not in the list of domains.

It is available only on the Product entity.

includeOnly Edit

The includeOnly filter should be given a comma-seperated list of integers, each representing an entity id.

It restricts the result set to only those entities specified in the list, as an alternative to fetching each entity with multiple seperate requests.

It is available on all entities.

domainRoles Edit

The domainRoles filter should be given a comma-seperated list of integers, each representing a permission role (see the Entity Reference enum for a complete list).

Not to be confused with inDomainRoles, which is a seperate filter.

It restricts the result set to only those domains with which the user is active in one of the specified roles.

It is available only on the Domain entity.

domainTypes Edit

The domainTypes filter should be given a comma-seperated list of integers, each representing a domain type (see the Entity Reference enum for a complete list).

It restricts the result set to only those domains of one of the given types.

It is available on the Domain and Theme entities.

productTypes Edit

The productTypes filter should be given a comma-seperated list of integers, each representing a product type (see the Entity Reference enum for a complete list).

It restricts the result set to only those products of one of the given types.

It is available only on the Product entity.

memberOnly Edit

The memberOnly filter should be given a boolean.

It restricts the result set to only those companies of which the user is a member.

It is available only on the Company entity.

merchiOnly Edit

The merchiOnly filter should be given a boolean.

If enabled, it restricts the result set to only those entities which are considered platform global.

It is available on the Product and Category entities.

inbound Edit

The inbound filter should be given a boolean.

If enabled, it restricts the result set to only those Invoices that the user is a client (or in a client company) of.

It is available only on the Invoice entity.

savedByUser Edit

The savedByUser filter should be given an integer representing a User id.

It restricts the result set to only those Products that the specified user has “saved”.

It is available only on the Product entity.

receiverId Edit

The receiverId filter should be given an integer representing a User id.

It restricts the result set to only those Shipments that the specified user is the receiver of.

It is available only on the Shipment entity.

asRole Edit

The asRole filter should be given an integer representing a permission role (see the Entity Reference Enum for a complete list).

It restricts the result set to only those EnrolledDomainss that are of the specified role.

It is available only on the EnrolledDomain entity.

Protocol Edit

todo

Authentication Edit

You need to be authenticated for all API requests. You can generate an API key in your developer dashboard.

Add the API key to all requests as a GET parameter.

Nothing will work unless you include this API key

Domain Roles Edit

Each domain user is assigned a domainRole. The domainRole determines which permissions the user has on the domain and it’s associated entities.

Type Value Description
PUBLIC 0 Publice has no permissions on the domain.
ADMIN 1 Admin has all permissions on the domain and it’s associated entities.
SALES 2 Sales has limited permissions on the domain and can add/edit/delete jobs, invoices, products, users and shipments.
DESIGNER 3 Designer can only edit and update job specific attributes related to job drafting and some specific job production attributes.
SUPPLIER 4 Supplier can only edit and update job assignments which they’ve been specifically assigned to.
CLIENT 5 Client can access and edit (some) job attributes where they are the assigned client or in the assigned client company.
MANAGER 6 Manager has limited permissions on the domain and can add/edit/delete jobs, invoices, products, users and shipments.
ACCOUNTANT 7 Accountant can access invoices, jobs and shipments.
THEME_EDITOR 8 Theme editor can create themems, edit them and apply them to the domain.

Domain Types Edit

Each domain is assigned a domainType. The domainType determines the type of products that can be made and sold by the domain. It can also determines which features are restricted or made available.

Type Value Description
UNRESTRICTED 0 Can create all product types and has access to all features
SELLER 1 Freemium teir domain type can only create: SELLER_MOD and CHAINED_SELLER_MOD product types. This domain type is restricted from updating and customising themes, adding domain team members and selling products on the Merchi marketplace
SELLER_PLUS 2 Can create the following product types: SELLER, SELLER_MOD and CHAINED_SELLER_MOD. This domain type is restricted from selling products on the Merchi marketplace.
SUPPLIER 3 Can create all product types. This domain type can only sell products on the Merchi marketplace by invitation.
RESTRICTED_SUPPLIER 4 Can create all product types. This domain type is restricted from selling products on the Merchi marketplace.
DOMAIN_SUPPLIER 5 Can not create any products. This domain type is created for third party suppliers to allow them to convert their production quotes into invoices and take payments. This domain type can be upgraded to a RESTRICTED_SUPPLIER on subscription.

Errors Edit

Code Name Description
200 OK Success
201 Created Creation Successful
400 Bad Request We could not process that action
403 Forbidden We couldn’t authenticate you

All errors will return JSON in the following format:

{
  "error": true,
  "message": "error message here"
}

Entity Reference Edit

The complete list of merchi entities is available here.