API Endpoint

The base endpoint for the API is:


Attempting to access the API over non-TLS / SSL connections will result in a 403 Forbidden response. The API will not attempt to redirect or upgrade your connection; instead, it will be expressly denied.

API Versioning

The major API version is specified in the API endpoint URL. Minor version changes are not included in the API or within any headers. When calling the API, you will always interact with the latest API minor version published for the major version specified in the URL.

At present, the only valid major API version is 1.

The following types of changes are considered minor and backwards-compatible. Your applications should be prepared to handle the following changes without prior warning:

  • Addition of new resources and endpoints
  • Addition of new properties to existing resources
  • A change in order of parameters within responses
  • Addition of new query string parameters, filters and options.
  • The length or format of system-generated IDs used as resource identifiers
  • Increase in functionality / features
  • Bug fixes: correcting API behavior that is providing incorrect or inconsistent results

The following types of changes are considered breaking changes and will be deployed as part of a new major version:

  • Removal of resources
  • Removal or name change of properties
  • Fundamental change in application behavior that is in conflict with existing behavior
  • Format of key data elements such as dates and times
  • Other types of changes that would generally be considered breaking and incompatible with existing applications

We take great care in applying changes to production environments to ensure that you can build applications with confidence that they will continue to work without interruption.


Authentication is done using OAuth 2.0 Bearer tokens. To make a request, you must include the token in the Authorization header of the HTTP request. The token is prefixed with Bearer as shown in the example HTTP call below:

POST /api/v1/carts HTTP/1.1
Host: api.comecero.com
Authorization: Bearer secret.live.ni295MF6Fg8KFo7ak90sKqLPHlRp40UH88Ypw4Lzfx2.1447194286.1b3d252fd6
Content-Type: application/json

{ ... }

Token Types

The API has three different types of tokens, each explained below.

Secret Tokens

Secret tokens are designed for server to server communication and should be kept secret, confidential and secure. They provide a powerful access mechanism to your resources and should be treated carefully. The importance of keeping these tokens secure cannot be overstated.

A secret token can be limited in scope so that it only has access to specific resources and methods. Additionally, a secret token can have an associated expiration date. We recommend that rather than creating "super" tokens with access to all resources and methods, you create tokens for specific purposes and applications and limit the permissions associated with a token on an as-needed basis. You can change the permissions associated with a secret token at any time.

The easiest way to create a secret token is from within your account under Developer> Tokens. Alternatively, you can generate secret tokens through the API.

Limited Tokens

Limited tokens provide the ability to build incredibly powerful client-side applications for your customers / shoppers. While not exactly the same, Limited tokens are similar to the concept of a session ID in server-side applications.

Limited tokens should never be hard-coded into a client side application (except for convenience in local testing / development). Instead, client-side applications should generate a new Limited token for each user and store it in the user's cookies, thereby enabling each user to have a unique token, which allows them to read and modify resources they create (such as carts and orders). Limited tokens can be easily be generated from client-side code with a simple API call.

Limited tokens have the ability to perform a large variety of operations, but are only authorized to perform those operations against specific customer-owned resources. Anyone can obtain a limited token, but limited tokens are scoped to the user who obtains the token.

Perhaps the best way to understand the nature of limited tokens is with an example:

A user makes a request for a new limited token. No authorization or authentication is necessary to obtain a new token. This new token has very limited permissions, for example, permission to read public products from the account that issued the token. Using the token, it can browse the accounts products and use them to create a new shopping cart.

When a shopping cart is created using a Limited token, the cart is "bound" to that Limited token, so the Limited token's permissions have grown to include the ability to update that shopping cart. Naturally this does not mean it can update other shopping carts, just the cart it created. You don't need to take any action to manage these permission changes, this is all automatically handled in the background for you.

Beyond the ability to modify the cart it created (adding and removing products, adding customer details, etc.), the limited token can also submit a payment for the cart. After a successful payment, the limited token then has access to read the associated order and other associated resources (such as subscriptions, invoices, etc.).

Limited tokens are specifically designed to allow you create ecommerce applications that are 100% client-side. Client-side applications allow you to rapidly test and deploy new features, allow developers who are "mostly designers" to be incredibly productive without any assistance from back-end developers, can provide extraordinary performance and delightful user experiences.

Of course, you are not required to build client-side applications. But when you combine client-side applications with the Comecero hosting environment, you maintain full control of your application while offloading nearly all aspects of PCI compliance to us.

Limited tokens expire approximately 14 days after last access. This allows customers who start shopping sessions to return to them after long delays and continue without interruption.

Trusted Tokens

Trusted tokens are designed to be used within client-side environments, but can have all of the power that a secret token has. The primary difference between trusted tokens and secret tokens are:

  • Trusted tokens cannot be created explicitly through the control panel or through the API. Instead, they are created and assigned to a user when signing in through the Comecero Sign-in page.
  • Trusted tokens have a short expiration: Approximately 60 minutes after the last use, a trusted token automatically expires.
  • The permissions assigned to a trusted token is directly inherited from the user to whom the trusted token is assigned. In other words, when a user signs in, they will receive a trusted token with the same level of permissions assigned to that user.

By default, a user is redirected to the Comecero Admin control panel after signing in. However, if you develop custom applications for internal users, you can register the URLs of these applications with your account and have internal users redirected to these applications after signing in via the OAuth 2.0 implicit grant specification. Upon successful sign in, the user is redirected to your application's receiver and a bearer token is included in the URL. Your application can then use this token to perform requests on behalf of the user.

The trusted token assigned to a user receives all the permissions assigned to that user. It is not possible to change the access level of a trusted token directly; any access changes must be applied to the user which will then be applied to the next trusted token assigned to that user.

Trusted tokens expire after 60 minutes of non-use. After the token is expired, the user can be directed back to the Comecero Sign-in page, and then redirected back to your application.

To redirect your users to a custom location, follow the instructions below:

  1. Create the application
  2. Add the OAuth 2.0 receiver URL to your account's general settings.
  3. Direct your users to the following URL, replacing https://example.com with your redirect URL


Note that the redirect URL you provide in the URL must be exactly the same as the URL provided in your account settings. If an unregistered URI is provided in the redirect_uri query string parameter, after the user successfully logs in, the sign-in page will display an error indicating that the redirect is not allowed. (Note that the error message is not shown until after successful login to prevent unauthorized parties from discovering valid redirect endpoints, but a redirect to an unregistered URI will never occur.)

Additionally, instead of registering custom redirect URLs within your account, as an alternative you can create apps from within your account (under Developer> Apps) which provide more flexibility in configuration. Apps created this way support both OAuth 2.0 implicit grant for client-side applications and authorization code grant flows for server-side applications. You can read more in Custom Applications.

Content Types

With the exception of endpoints that support file uploads, the API works exclusively with JSON, both for requests and responses. All requests that contain a payload (but are not file uploads) must include the following header:

Content-Type: application/json

Error Handling

All errors return a consistent JSON response that makes it possible to parse and use for presentation in you application. In addition, all error responses will include a standard HTTP status code.

As best as possible, we use generally-accepted HTTP status codes for responses that should match the error scenario.

You can be confident that responses in the 2xx range represent success, responses in the 4xx and 5xx range represent errors. We do not return status codes in the 1xx or 3xx ranges.

Here is a summary of the HTTP status codes you can expect to receive:

Status Description
200 The request was completed successfully.
201 The request was completed successfully and resulted in the creation of a new resource. The URL to the newly created resource is included in the Location response header.
204 The request was completed successfully, and the response does not include any content. This is most often seen with methods that delete resources.
400 Invalid input. The error message will contain details and instructions on what caused the error and how to resolve it.
401 Unauthorized. Your credentials are missing or invalid.
402 Used exclusively for failed payments. Indicates that an attempt to process a payment was unsuccessful.
403 Forbidden. Your supplied credentials are either expired or don't grant the level of access necessary to make the request. See the error response for more details.
404 Resource not found. See the error response for more details.
405 Method not allowed. The endpoint in your request does not support the method in your request.
409 Conflict. The request could not be fulfilled based on a conflicting state, status or configuration. See the error response for more details.
422 Unprocessable. The request could not be completed based on existing conditions. See the error response for more details.
429 You have exceeded the maximum request velocity.
500 Internal Server Error. See the error response for more details.
502 External Server Error. This is most often used when an external payment gateway generated an unhandled error.
504 External Server Timeout. This is most often used when an attempt to communicate with an external payment gateway resulted in a timeout.

Errors will be returned as a JSON object as shown in the example below:

  "error": {
    "type": "not_found",
    "code": "resource_not_found",
    "message": "Product not found: 1001 does not exist.",
    "reference": "3ynx9HZ",
    "status": 404,
    "meta": null

A description of each field is described in the table below.

Property Description
type A string that is a text represenatation of the HTTP status code.
code A code that provides a more specific error category for the error type. See the table below for a list of possible codes.
message A human-readable explanation of the error which generally includes information about how to fix the error, when applicable.
reference A reference to the specific error. If communicating with support about this error, please include this reference.
status The HTTP status code
meta Not used often, but in some cases is used to convey additional information about the error.

Errors can be further subdivided by examining the provided error code, as outlined in the table below. While it is not common, we may from time to time add additional error codes. Therefore, your application should use a catch-all for each HTTP status code in the event that a new error code is returned.

Code Status Type
invalid_input 400 bad_request
Something in your request does not pass validation requirements.
invalid_syntax 400 bad_request
Your request could not be processed due to invalid syntax. Most likely the provided JSON is malformed.
invalid_promotion_code 400 bad_request
A promotion code has been provided that is invalid, expired or otherwise cannot be applied to a cart or invoice.
token_type_mismatch 400 bad_request
Generally used when attempting to access a live resource with a test token, or vice-versa.
invalid_token 401 unauthorized
The provided token is invalid.
invalid_login 401 unauthorized
The login attempt was unsuccessful.
account_locked 401 unauthorized
The account is temporarily locked and cannot be accessed. This occurs after a large number of unsuccesful login attempts and will be automatically removed after 10 minutes.
tls_required 401 unauthorized
If attempting to access the API over an insecure protocol, this error will be returned. Always communicate with the API using https://.
payment_declined 402 transaction_error
The payment was attempted but was unable to be processed.
invalid_cvv 402 transaction_error
A credit card payment was rejected due to an incorrect CVV / security code value.
invalid_avs 402 transaction_error
A credit card payment was rejected due to an incorrect billing address.
transaction_failure 402 transaction_error
Generally used when a follow-on transaction such as an error attempting to process a refund, capture or void.
insufficient_permissions 403 forbidden
Generally used when attempting to access a resource or method that is beyond the scope of the supplied token. This means the token is valid, but not for the request performed.
invalid_origin 403 forbidden
Returned when attempting to call the API from a client-side origin that has not been approved / white-listed in your account settings.
resource_not_found 404 not_found
A requested referenced resource was not found.
in_use 409 conflict
Returned when attempting to create a resource or set a property that is valid but already is use or taken.
duplicate 409 conflict
Used as a warning that a payment is an exact duplicate of a payment recently procssed. The duration of the duplicate window can be configured in your account settings and can be overridden with an API flag. Duplicate checks can also be disabled completely.
feature_not_enabled 409 conflict
Used to indicate you are attempting to use a feature that has not been enabled for your account.
binding_limit_exceeded 422 unprocessable
Used when a limited token is bound to a large number of distinct resources. This error is generally the result of misuse of limited tokens, for example, inadvertently using the same limited token for multiple customers. Proper use of limited tokens should prevent this error from ever occuring.
invalid_state 422 unprocessable
The requested change cannot be performed because the resource is in a status that makes it incompatible with the request. An example is requesting to refund a payment that was previously refunded.
request_failure 422 unprocessable
Used very rarely. A scenario occured that prevented the request from being processed successfully. See the error message for more details.
account_limit_exceeded 422 unprocessable
When a limit of your account has been met or exceeded. For example, you are attempting to upload a file larger than permitted by your account.
request_limit_exceeded 429 too_many_requests
Your usage of the API has exceeded maximum limits. You will need to decrease the velocity of requests, or pause between groups of requests.
unspecified_error 500 internal_server_error
An error occured on the server and no further details will be provided. If you access this error repeatedly, please contact support for assistance.
bank_network_error 502 external_server_error
An error was returned by a remote gateway or payment processor.
remote_server_error 502 external_server_error
An error was returned when attempting to communicate with an external service that is required to complete the request.
bank_network_timeout 504 external_server_timeout
A timeout occured when attempting to communicate with a remote gateway or payment processor.
remote_server_timeout 504 external_server_timeout
A timeout was returned when attempting to communicate with an external service that is required to complete the request.

Expanding Objects

Throughout the API you will see URLs to other resources. These links provide convenient ways to access related and associated data.

To increase performance and reduce the number of requests necessary to get the data you need, we've made it possible to expand URLs as you need. For example, imagine the following response:

   "this":"is a string",

To access something_else, you can either perform a GET on the supplied URL, or you could request to expand it in your original request, which would return the following:

   "this":"is a string",
      "here":"is the other string"

To expand an resource, just provide a comma-separated list of the items you would like expanded in your response in the expand URL query string parameter, like this:


This will return the cart resource, including a paginated list of all payments submitted for that cart within the payments property.

You can also expand nested objects using "dot notation" to address the nested resources. Continuing the example above, if you would like to expand the payment_method resource within the list of payments, your request would look like this:


This will expand the payments, and expand the payment_method within payment.

A few more examples:

Expand a product within a the items of a cart:


Expand products and a customer's payment methods within a cart:



For performance reasons, you can expand a maximum of ten (10) items when requesting a single resource (such as a cart or a payment) or a maximum of two (2) resources when requesting a list of items (such as a list of carts or list of payments).

Additionally, for performance reasons, it is not possible to expand lists within lists. For example, if you are requesting a list of carts, it is not possible to expand the list of payments within the list of carts. To expand the list of payments for a cart, you should perform a GET on a single cart and request to expand the list of payments for that cart.

Last, the maximum depth of an expand request using "dot notation" is three (3). For example, if requesting an order object, expanding customer payments is valid: expand=customer.payments (two levels deep) but expanding customer payments cart items product is not: expand=customer.payments.cart.items.product (five levels deep). This helps improve performance and reduces circular and / or redundant data in responses.

Error Handling

Requests that include more expand values than allowed will not throw an error but expand values beyond the allowable maximum will be ignored. This means that if you request to expand twelve resources and the maximum allowable for that request is ten, the last two expand values will not be applied.

Additionally, expand requests that include invalid values (such as a misspelling or incorrect "dot notation") do not throw an error but are simply ignored.

Lists and Pagination

Most resources can be retrieved in lists. There are two types of lists: offset-based and cursor-based.

Offset-based lists are used for lists that are finite in nature, and cursor-based lists are for lists that are "infinite" in nature. For example, your account will have users, and most likely you'll hit a point where the number of users remains relatively static (you might add or remove users from time to time, but the number of users will be relatively small, even if counted in the hundreds or thousands). Pagination in offset-based lists is done by providing an offset value, which is a simple count of where to start a page relative to the first item in the list.

Cursor-based lists are used when the items within the list will most likely continually grow over time, such as orders or payments. Unless the account is abandoned, the list will in theory get larger and larger forever. Pagination in cursor-based lists is done by providing a cursor (which is the ID of an item in a list) and the page starts either before or after that item, as specified in the request.

Cursor-based lists provide performance increases over offset-based lists when the number of pages is large.

The same resource may exist in both offset-based and cursor-based lists, depending on the type of list. For example if requesting a list of all payments for your account, the list will be cursor-based. However, if asking for a list of payments from a specific customer, the list will be offset-based.

Below is an example of an offset-based list (data removed for brevity):

  "total_items": 227,
  "total_pages": 3,
  "current_page": 2,
  "next_page_url": "https://api.comecero.com/api/v1/products?offset=200",
  "next_page_offset": 200,
  "previous_page_url": "https://api.comecero.com/api/v1/products?offset=0",
  "previous_page_offset": 0,
  "current_page_url": "https://api.comecero.com/api/v1/products",
  "data": [ { ... }, { ... } ]

Offset-based lists usually allow additional parameters and filters (such as the sort_by). The API documentation for each list specifies the options available to further refine and shape your list.

And here's an example of a cursor-based list (data removed for brevity):

  "next_page_url": "https://api.comecero.com/api/v1/carts?after_item=0090199635907Ca4VeGDhA&desc=true",
  "next_page_after_item": "0090199635907Ca4VeGDhA",
  "previous_page_url": "https://api.comecero.com/api/v1/carts?before_item=0090699532787Ca10uBZT3&desc=true",
  "previous_page_before_item": "0090699532787Ca10uBZT3",
  "current_page_url": "https://api.comecero.com/api/v1/carts?after_item=0090700560277CaPTKLt05&desc=true",
  "data": [ { ... }, { ... } ]

You'll notice that the cursor-based list provides little context for where you're at in the list. To address that, you can provide a date that your list should start after or before with URL parameters in your request, like this:


Or a date range for the list:


By default, the date targeted by date_start and date_end will be the date_created in your list of resources. However, if you would rather have your list based on the date the items were modified, you can indicate that with the "date_type" parameter. For example, this would give you all carts modified in January:


Some cursor-based lists provide additional features such as filters that allow you to refine what items are returned in your results. See the API documentation for the specific endpoint for more details.

Regardless of the list type, the following two paramters are always possible:

  • limit: the number of items to return per page. The default and maximum is 100.
  • desc: indicates if the list should be returned in descending order (true or false). The default is false.

Dates and Times

Format in Response Values

The format used for dates and times in API responses follows the standard set by the ISO 8601 standard.

An example of a date provided by the system is below:


The date is represented by the first 10 characters followed by a delimiter T, followed by the time in 24-hour format, followed by the offset from UTC. If the time is presented in UTC, the offset will always be Z. Otherwise, it will be a +/- value indicating the difference from UTC in time.

To make it easier to work with local dates and times, it is possible to append a timezone parameter to the URL of any request. When a valid timezone is provided, all dates and times in the response will be provided in the supplied timezone and will handle daylight saving time properly.

The value for timezone can be any valid timezone from the IANA / Olson timezone list. For example:


An example of a date and time formatted in a local timezone is below. Note the offset from UTC is represented by the number of hours the time is ahead (+) or behind (-) UTC. The offset value can be whole (for example, 01) or partial (for example, 01:30) hours.


If the offset is a fractional hour (as is the case for some timezones), the offset will contain the fractional hour, as shown below. This example is from the Asia/Kolkata timezone:


If the timezone value is not supplied, the default timezone (as specified in your account settings) will be used. The initial value for your account default timezone is UTC.

If you provide a timezone parameter and that timezone is invalid, an error will occur.

Format in Request Values

The format used for dates and times in API requests can follow one of two options: the ISO 8601 standard format, or our "shorthand" format (see below for details).

ISO 8601

When providing dates in requests, you can provide dates using the same format described in Response Values above.

The offset / timezone of the provided date will be determined by using the following priority:

  1. Look for an offset in the supplied date.
  2. If an offset is not provided, look for a timezone in the supplied query string.
  3. If a timezone is not provided, use the account default timezone.

Shorthand Format

Our shorthand format makes working with input dates more convenient, especially when designing user interfaces.

Instead of providing explicit dates, you can instead use variables that cover common use cases.

A list of allowable variables can be found below:

Variable Description
today The first moment of today when providing a date_start, otherwise, the last moment of today.
yesterday The first moment of yesterday when providing a date_start, otherwise, the last moment of yesterday.
this_week The first moment of this week when providing a date_start, otherwise, the last moment of this week. Sunday is considered the first day of the week.
last_week The first moment of last week when providing a date_start, otherwise, the last moment of last week. Sunday is considered the first day of the week.
this_month The first moment of this month when providing a date_start, otherwise, the last moment of this month. Leap years are properly handled.
last_month The first moment of last month when providing a date_start, otherwise, the last moment of last month. Leap years are properly handled.
this_year The first moment of this year when providing a date_start, otherwise, the last moment of this year.
last_year The first moment of last year when providing a date_start, otherwise, the last moment of last year.
+x Add the supplied number of days to today. The result is the first moment of the resulting day when providing a date_start, otherwise, the last moment of the resulting day. You can supply any integer between 0 and 9999.
-x Subtract the supplied number of days from today. The result is the first moment of the resulting day when providing a date_start, otherwise, the last moment of the resulting day. You can supply any integer between 0 and 9999.
yyyy The first moment of the supplied year when providing a date_start, otherwise, the last moment of the supplied year.
yyyy-MM The first moment of the supplied month and year when providing a date_start, otherwise, the last moment of the supplied month and year.
yyyy-MM-dd The first moment of the supplied day, month and year when providing a date_start, otherwise, the last moment of the supplied day, month and year.
yyyy-MM-ddTHH The first moment of the supplied hour, day, month and year when providing a date_start, otherwise, the last moment of the supplied hour, day, month and year.
yyyy-MM-ddTHH:mm The first moment of the supplied minute, hour, day, month and year when providing a date_start, otherwise, the last moment of the supplied minute, hour, day, month and year.

For example, if you'd like to get a list of carts created yesterday, you could do the following:


When yesterday is provided for date_start, it would be translated to yesterday's date at 00:00:00 (or 12:00 AM). When yesterday is provided for date_end, it is translated into yesterday's date at 23:59:59 (or 11:59:59 PM).

You could do the same request using +/- variables, like this:


This subtracts one day from today's date, which ends up being the exact same as the previous example.

Here's a report for this year:


A report for the last 7 completed days:


A report for the last 7 days, including today:


Here's a report for all of 2014:


Occasionally, you'll need to provide dates that are not a part of a date range (such as an invoice due date or a token expiration date) in a request body. As indicated in the table above, all shorthand dates other than date_start dates will be treated as the last moment of the provided date. For example, if you provided +30 as the due date for an invoice, the date would be converted to the last moment of the day that is 30 days from today (i.e. 23:59:59 or 11:59:59 PM).

Meta Data

Virtually all major resources allow you to append custom meta data. This provides flexibility for you to extend the provided data set with custom values.

When adding meta data, you can provide a simple string or numeric value, or you can provide a complex object. It is generally recommended to provide an object, even if you only need to supply a single value so you'll be able to add additional values in the future if necessary.

The value provided for meta must be 2048 characters or less.

Here's an example of a simple meta data value:

"meta": "my-value"

And here's a more complex object:


And even more complex:


Using meta data with carts, invoices and orders

When you supply meta data to a cart or an invoice, the value supplied will be copied to the order that is created when the payment is completed. This is also true for cart items and invoice items: any meta data provided with a cart item or invoice item will be copied to the order item when the payment is completed. This allows you to collect and append custom data during a checkout process and have those values stored with the order when payment is completed.

Naturally you are free to change or remove the meta data that is automatically attached to an order.

Customizing Output

The API allows you customize the response objects through two URL parameters: show and hide. Using these parameters allows you to trim down the size of response objects to the items that you need, which can increase performance. This is particularly true when requesting lists of objects.

The show parameter allows you to specify which properties you want to receive in the response. With the exception of the main resource URL, nothing other than the items you specify in the show parameter will be included in the response.

The hide parameter does the opposite. All properties in the resource will be returned with the exception of those specified in the hide parameter.

You cannot use these parameters at the same time: If you supply show you cannot supply hide and vice-versa.

The values are supplied as a simple comma-separated list of items you want to show or hide, like this:


You can target nested items using "dot-notation" to address the items you want to show or hide, like this:


This also works for items that are nested within expanded objects, like this:


These parameters combined with expanding objects allow you to get back exactly what you need in your application and nothing more in a single API call.


The API fully supports CORS for all API endpoints. This allows you to build client-side applications that you can host and run from within your own environment or from within something like an AWS S3 bucket.

What is CORS, and why do I care?

With JavaScript, a browser can interact with an API over HTTP. This technique, often referred to as Ajax, allows for powerful and flexible client-side applications and is used in a huge number of modern web sites and applications today. There's a reason for the shift towards client-side applications: They allow fast, responsive and "app-like" experiences, allow for rapid changes and additions to user interfaces and experiences, provide a clear separation between server-side and client-side portions of the application, and empower people who are "mostly designers" to get more deeply involved with the development and implementation of an application's user experience without having to dive into back-end code.

For security reasons, browsers do not allow Ajax requests to be made to servers that have a different origin than the one the page is served from. This is known the Same Origin Policy and provides significant security benefits. So, if you have a web page running on your-domain.com and you wanted to make a call to an API located at other-domain.com, the browser would not allow the request and response to process successfully. Read more about why the same origin policy is important.

CORS to the Rescue

As the web evolved, it became clear that there were a significant number of legitimate use cases for cross-origin Ajax requests. Turning off the same-origin policy would disable one of the fundamental security models of the web, so a new system was developed to allow cross-origin calls to be made securely: cross origin resource sharing or CORS.

CORS allows a server to say "I will allow cross-origin resource requests, but only from these origins". This allows a web server to "opt in" to a cross origin request, and when doing so can make sure that the endpoints it exposes to cross-origin calls are prepared for the additional security requirements and can limit the origins from which requests can be made. CORS also allows a server to provide a "wildcard" response, which says "I will allow cross-origin requests from any origin".

CORS and the Comecero API

For security reasons described above, we only allow CORS requests for your account from origins that you explicitly permit. You can easily manage your origins through your account settings (under Settings> General) or through the API directly. Note that all origins you intend to use need to be listed or your requests to the API from the browser will not be successful. This includes origins that you use in local development. The origins you provide do not have to be valid in the real-world (for example, if you use not-a-real-domain.com in local development). However, any origin that you intend to use must be listed or your requests will fail.

For convenience in development and testing, requests from and localhost are always permitted even if not listed within your account.

It is important to note that while all modern browsers support cross-origin calls, some older browsers do not. Depending on your audience, this may or may not be an issue. For internal applications (i.e. tools for internal employees or users) you can generally control or mandate that only modern browsers are used to access the application. This is not the case with external applications (such as shopping carts, checkout and customer management apps).

Additionally, many cross-origin calls are subject to a "pre-flight" request to confirm with the server that the origin is approved before the request is made. This adds a small measure of performance overhead as it increases the communication that must be done with the API when making requests.

To address this, Comecero provides a robust and high-performance hosting environment for client-side applications. When you host within this environment, calls to the API are automatically proxied and therefore not subject to cross-origin restrictions because the hostname of the API and application are the same. Additionally, the Comecero hosting environment is fully PCI-compliant, which means by hosting your applications within Comecero, you greatly reduce your PCI burdens. This hosting environment is provided at no additional cost to you and can support custom domains and SSL certificates. Read more about hosting custom applications here.

Partial Updates

Most updates to resources only require updating a small number of the properties within the resource. We follow a simple pattern to allow partial updates through the API.

To perform a partial update, limit the request to the properties you want to change. Properties that are not included in the request will remain unmodified. For example, if you want to update a customer's email address only, your request might look like this:

"email": "ann.price@example.com"

The above request would update the customer's email address to the new value, but would leave any other customer properties such as the name and address unchanged. You can include as many properties you want in a partial update.

NULL values

Note that providing null is not the same as leaving out the property. If you include a property in the request and the value is null, the property will be updated and set to null.

Updates and partial updates are performed by using the HTTP POST method to the resource URL. Any properties included in the request that are not a part of the request object are ignored. Including properties that are not a part of the resource will not generate an error, instead, those properties are simply discarded.

Localized Formatting

Support for localized formatting is provided for the properties with the following data types: currency / money, dates / times and percentages.

By default, formatted values are not output with resources. If you would like to receive formatted values, simply append formatted=true to the URL of any API request. The output will include an additional object named formatted that includes all properties that are eligible for formatting, using the same property name.

For example, here is a portion of a cart response without formatting:

  "object": "cart",
  "cart_id": "0092790998816CagdfyG8B",
  "date_created": "2015-12-10T23:16:38Z",
  "date_modified": "2015-12-10T23:17:57Z",
  "currency": "USD",
  "subtotal": 119.9,
  "shipping": 5,
  "discount": 0,
  "tax": 9.38,
  "total": 134.28

And here's the same cart with the the formatted=true option supplied:

  "object": "cart",
  "cart_id": "0092790998816CagdfyG8B",
  "date_created": "2015-12-10T23:16:38Z",
  "date_modified": "2015-12-10T23:17:57Z",
  "currency": "USD",
  "subtotal": 119.9,
  "shipping": 5,
  "discount": 0,
  "tax": 9.38,
  "total": 134.28,
  "formatted": {
    "subtotal": "$119.90",
    "shipping": "$5.00",
    "tax": "$9.38",
    "total": "$134.28",
    "date_created": "12/10/2015 11:16 PM",
    "date_modified": "12/10/2015 11:17 PM"

As you can see, you can now easily access unformatted and formatted values in code like this:


What locale and culture is used for formatting?

We use a variety of techniques to determine the how to format the values according to the logic below:

For requests in client-side environments

You don't need to do anything; the locale is detected automatically from the browser settings. You can, however, override the automatic selection by providing user_locale in a query string URL parameter in the API request the same way described in the server side environment below.

For requests in server-side environments

You can provide the user's locale as a URL query string parameter to any API request using the two-character ISO 639-1 language code, like this:

For French: user_locale=fr

For German: user_locale=de

You can also use extended codes to give more precision, for example:

For French / Canada: user_locale=fr-CA

For German / Austria: user_locale=de-AT

For English / United States: user_locale=en-US

As a best practice in server-side requests, simply pass through the entire browser Accept-Language property in the user_locale URL query string parameter and the API will parse it and automatically choose the proper formatting to use.

If no user_locale is provided, the default account locale is applied.

Custom Applications

Comecero makes it possible to build completely custom commerce applications such as shopping carts, customer account management portals, internal administrative tools and more. Combining Comecero's API with your custom applications will allow you to rapidly build custom solutions without compromise.

Since Comecero provides a rich REST API, You can build custom applications using any language or framework that "speaks" HTTP. You can build your apps using a server-side or client-side approach, or a combination of both.

Custom Client-Side Applications

For client-side apps, Comecero provides a high performance, redundant, secure and PCI-compliant hosting environment. This allows you to build custom applications locally and deploy them into production without any worries of servers, uptime or IT overhead. The Comecero hosting environment is available for all your custom client-side applications at no additional charge. It also provides support for custom domains and SSL/TLS so you can customize every aspect of your app's presentation to your users.

Client-side applications are typically built to run within a web browser using a combination of JavaScript, HTML and CSS. You can develop and test your custom application from your local development environment, and when complete, you simply zip it up, upload it to Comecero and in a few clicks install and deploy the app. Alternatively, you can use the API to publish and deploy the app, allowing you to completely automate the process.

To make testing easier, Comecero automatically versions all of your published apps and app revisions. If you deploy a new app and find out you are having problems with the new version, you can roll back to a previous version with a click of the mouse or simple API call.

Your Comecero account provides a full test environment, which allows you to deploy apps for testing purposes before they're production ready. This isolation gives you a safe sandbox to experiment without having to worry about disrupting your business.

You can also host your custom client-side applications on your own server, if you prefer. While most choose to host their shopping cart applications on Comecero (thus easing PCI burdens and requirements), you might find it convenient to host internal applications on your own servers. Using a simple OAuth mechanism, you can easily obtain client-side tokens for your internal users from within your internally-hosted applications. Check out this tutorial for more information.

Are you ready to create your first client-side hosted app? Follow this step-by-step tutorial and you can deploy a custom app to the Comecero hosting environment in literally minutes.

Custom Server-Side Applications

Of course, you aren't limited to client-side applications. Comecero provides the ability to create API tokens for use in server side environments with granular control over permissions, automatic expiration and more. This makes it easy to create new applications, hook Comecero's data and functionality into your existing applications, or grant specific access to partners and suppliers so they can interact with your ecommerce back end.

Comecero was built using an "API First" approach. This means we've exposed endpoints for virtually everything. All you need is an API token to get started. Just sign into your account and visit Developer> API Tokens to generate a token. Then take a look at our API Explorer to see what you can do.

Hosted Functions

Comecero allows you to run your own code in its secure, robust and highly monitored environment. This allows you to write code to handle specific scenarios to your exact specifications, thereby extending Comecero's platform.

You can provide your Hosted Functions in Node.js, Python, Java or C# (.NET Core), giving you flexibility in choosing a language that you are most comfortable with. Hosted Functions can be configured to run automatically in response to events you specify such as a new order created, a subscription cancelled or much, much more. You can also invoke Hosted Functions directly, perhaps from within code in your own systems.

Read the Hosted Functions tutorial to see how easy it is to extend Comecero with your own custom code.

Event Notifications

Event Subscriptions are real-time "messages" that you configure to be sent to your systems when specific event happen within the commerce system. These messages are sometimes referred to as "webhooks". You can configure events to be sent to your systems by HTTP(S) or email in JSON, XML or HTML. You can subscribe to as many events that are useful to you; we allow you to precisely subscribe to the specific events you need so that we don't flood your servers with data you don't care about. These events can be configured under Developer> Event Subscriptions. Click "Add" to get started.

You first specify the type of event you wish to subscribe to. For example, a common scenario is being notified when a customer places an order (the "Order Created" event). Choose the method you would like to use to have the data delivered to you (HTTP/S or email) and the destination (either a URL or email address).

Event notifications sent by HTTP/S are signed with an HMAC-SHA256 of the request payload. This value is included in the 'X-Comecero-Signature' HTTP request header when the request is sent to your server and can be used to ensure that the request came from Comecero and has not been modified in transit. By default, the key used to sign the payload is defined in Settings> Technical> Signature Key. You can override the default key used for the signature for each Event Subscription if you wish.

Also for HTTP/S destinations, you can configure HTTP Authorization to be sent with your request, allowing you to further restrict access to your endpoint. To further customize the request, you can add a custom HTTP header and value.

You can also add "environment variables" to the request to pass in other configuration data that your endpoint may find useful. The environment variables are supplied as the environment_variables property within the body of the event notification payload and the property is populated with the name / value pairs you provide in your event subscription, for example:

   "my_custom_property":"my custom value",

There are some additional optional fields that can be configured, such as Expand, Show, Hide. These allow you to shape the incoming payload so that all of the data you want is provided how you want it. For example, when you subscribe to an Order Created event, an event notification will be sent to your endpoint with the data property that contains the order object.

You'll note, for example, that the order object contains a customer. The customer object in the order payload by default is a URL to the customer object. If your endpoint requires the customer data, you can use the customer URL to perform a GET to obtain the customer details, but much easier is to tell the Event Notification you configure to simply expand it when it sends the data. To do this, in the Expand box, enter "customer" (sans quotes). If you want to also expand the payment, enter "customer,payment". You can also reference nested items to expand. For example, if you wanted to get details of the subscriptions for the order, you could do something like "customer,payment,items.subscription" (up to a maximum of 10 items to be expanded). You can also limit what is sent by using the Show and Hide parameters. For more details, see the section on customizing output.

Each event contains a property fingerprint that is a unique ID for the event that generated the notification. If you subscribe to the same event in multiple event subscriptions, when an event is triggered, each event notification will have a unique event_id, but will share the same fingerprint. This allows you to match the same event across multiple event subscriptions.

Failed Notifications and Retries

If our attempt to send you an event notification fails, we'll automatically retry to send the event using an "exponential backoff" approach, meaning the longer the event notification continues to fail, the longer we wait for the next retry. We'll attempt to deliver the message over several days. You can view failed events within your account under Developer> Event Notifications.

You can download a sample Order Created event notification here.

License Services

A license service allows you to configure a script to provide a license or registration code to customers after they purchase your products. You can create a license service for each product or share a single license service across multiple products. Setting up a license service is easy.

A license service can be configured to return a code from a list, or from a script hosted on a remote server. This document primarily considers the scenario where a code will be returned from a remote server. If you wish to provide a code from a list, simply sign into your account and go to Store> License Services, click "Add" and follow the instructions to configure your license service.


After a customer payment is successfully completed, Comecero will send a message via HTTP or HTTPS POST to a destination you specify. The HTTP request body will contain a license request object (see "Request Payload Sample" section) that includes details about the order, customer and item for which a license or registration code is being requested. You can configure the license request body to be sent in JSON or XML.

When your script receives a license request, it should take the supplied information and generate the details the customer needs to license or register their product and should output the value as the response body. The total length of the license code response must be 4096 characters or less.

The response can contain the following HTML tags for formatting purposes: <br>, <b></b>, <strong></strong>, <i></i>, <em></em>. Any other HTML characters will be stripped from the response.

Your response to the license request should return an HTTP status code between 200 - 299. Any other response status will be interpreted as a processing failure and the license request will be tried again.

The response you provide to the request will be saved with the associated item on the order and will be provided to the customer after the purchase on the receipt page and / or in the email order confirmation, based on your configuration.

Request Failures

If the license service fails to connect to the HTTP location and is either unable to successfully connect, does not receive a response within 30 seconds or receives an HTTP status code outside of 200 - 299, the license service will consider the request to be a failure and will queue the request to be tried again.

The license service will automatically retry to send the the request again using an "exponential backoff" approach, meaning the longer the request continues to fail, the longer it will wait for the next retry. We'll attempt to deliver the message over several days. You can view failed events within your account under Developer> License Requests.

You can manually request an immediate re-send of a failed or successful request from within the control panel or through the API. If you manually retry a previously failed request and it is successful, future retries will be automatically cancelled. If a manual retry is not successful, the retry schedule will continue as previously scheduled.

You can configure an Event Notification to notify you by HTTP or email when a license request fails. This will help you know when your server or script is having a problem and you may need to take action to resolve the problem. It is important that issues are addressed quickly as your customers will be unable to have their orders fulfilled while requests are failing.

If an order has failed license requests, the order confirmation email to the customer will be queued until all license requests have been successfully processed. Immediately upon successful request of all license requests in an order, the order confirmation email will be sent to the customer.

Securing Your Script

There are a few ways you can secure your script from unauthorized access: HTTP basic authentication, custom HTTP headers and request signature.

To secure your script with HTTP basic authentication, simply configure the script to require a username and password by HTTP basic authentication and then supply those values where prompted when creating the license service in the control panel. Every request will contain the credentials you provide, helping ensure that the request is coming from an authorized source.

To secure your script with custom HTTP headers, you can configure your script to only respond to requests that include a custom HTTP header and value that you specify.

You can further verify the authenticity of the request and that it has not been modified by examining the request signature. License requests are signed with an HMAC-SHA256 of the request payload. This value is included in the 'X-Comecero-Signature' HTTP request header when the request is sent to your server and can be used to ensure that the request came from Comecero and has not been modified in transit. The key used to sign the payload is defined in Settings> Technical> Signature Key.

You must not attempt to authenticate the request by using the IP address of the server making the request. Comecero is deployed in a highly-distributed environment and the IP address of the sending server will vary from request to request.

Build Your License Service

The script that processes the license request can be as simple or complex as you need. At a minimum, the script should:

  • Receive and parse the request payload (JSON or XML) that will be sent by HTTP or HTTPS POST.
  • Using the supplied data, generate a license or registration code using your algorithm or system.
  • Return the value you would like provided to the customer as a response.
  • Provide an HTTP response status code within the range of 200 - 299.

Your response can include basic HTML for formatting. See the "Overview" section for more information.

See the section "Request Payload Sample" at the bottom of this document for an example of the request payload.

You'll notice two important things about the request body:

  • The request contains a property "product_id" that indicates the product for which the request is being made. If you have the same script that handles requests for multiple products, this will tell you the product for which the request is being made.
  • The request contains an object "order" which contains the full order details of the customer's order, including an object "customer" that contains the customer data, which you may need to generate the license.

Host Your License Service

Most users choose to host their license service on their own servers. However, you can choose to host it within the Comecero infrastructure if you wish using a Hosted Function.

Configure Your License Service

Once you have built and deployed your license service, you can configure it within the control panel.

  • From within your account, navigate to Store> License Services and click "Add".
  • Give the license service a name for your reference, for example "Product A License Code Service"
  • Provide a label. What do your call your license? A license code, registration code, unlock code, etc. This will be used to describe the code when sent to your customers.
  • Select "From a remote URL"
  • Provide the URL to your script. The script can be HTTP or HTTPS.
  • Provide HTTP authorization and a custom header if you have configured your script to use those values (see Securing your Script for more information).
  • Select your payload format: JSON or XML. This indicates your preferred format for the request body we send with each request.
  • Optionally provide any instructions you would like to provide to your customers along with the license. For example, this might be instructions on how to use the provided license.
  • Provide a description of the license service, which are internal notes for your reference.

Click to add the license service. Once created successfully, navigate to a product under Store> Products that you wish to use the license service. Scroll down and locate the "Add License Service" button and click it. Find the license service and add it to your product. You can assign the license service to as many products you wish.

The next purchase made for your product will make a request to your license service.

Request Payload Sample

The below payload will be sent to your script as the request body via HTTP or HTTPS POST.

After the sample below, scroll down to view a response payload sample.

  "object": "license_request",
  "url": "https://api.comecero.com/api/v1/license_requests/0142130710483Lruow24Z6",
  "license_request_id": "0142130710483Lruow24Z6",
  "date_created": "2017-01-04T00:45:10Z",
  "date_modified": "2017-01-04T00:45:10Z",
  "test": true,
  "account_id": "AA0000",
  "status": "queued",
  "product_id": "1001",
  "license": null,
  "license_service": "https://api.comecero.com/api/v1/license_services/0129056741723Ls5nmtGy7",
  "order": {
    "object": "order",
    "url": "https://api.comecero.com/api/v1/orders/6622-NXKHE-858",
    "order_id": "6622-NXKHE-858",
    "date_created": "2017-01-04T00:45:10Z",
    "date_modified": "2017-01-04T00:45:10Z",
    "test": true,
    "account_id": "AA0000",
    "payment_status": "completed",
    "fulfilled": true,
    "currency": "USD",
    "subtotal": 79.95,
    "subtotal_original": 79.95,
    "shipping": 0.0,
    "shipping_original": 0.0,
    "discount": 0.0,
    "tax": 0.0,
    "tax_original": 0.0,
    "total": 79.95,
    "total_original": 79.95,
    "tax_inclusive": false,
    "discount_on_gross": false,
    "settlement_currency": "USD",
    "settlement_subtotal": 79.95,
    "settlement_subtotal_original": 79.95,
    "settlement_shipping": 0.0,
    "settlement_shipping_original": 0.0,
    "settlement_discount": 0.0,
    "settlement_tax": 0.0,
    "settlement_tax_original": 0.0,
    "settlement_total": 79.95,
    "settlement_total_original": 79.95,
    "customer_ip_address": "",
    "customer_ip_country": "US",
    "referrer": null,
    "affiliate_id": null,
    "promotion_code": null,
    "items_count": 1,
    "items_quantity": 1,
    "items": [
        "object": "order_item",
        "url": "https://api.comecero.com/api/v1/orders/6622-NXKHE-858/items/1001",
        "item_id": "1001",
        "date_created": "2017-01-04T00:45:09Z",
        "date_modified": "2017-01-04T00:45:09Z",
        "product_id": "1001",
        "order_id": "6622-NXKHE-858",
        "name": "Office Suite Pro",
        "type": "digital",
        "quantity": 1,
        "currency": "USD",
        "price": 79.95,
        "price_original": 79.95,
        "reference_price": null,
        "reference_currency": null,
        "subtotal": 79.95,
        "subtotal_original": 79.95,
        "discount": 0.0,
        "tax": 0.0,
        "tax_original": 0.0,
        "total": 79.95,
        "total_original": 79.95,
        "tax_rate": 0.0,
        "tax_code": null,
        "settlement_currency": "USD",
        "settlement_price": 79.95,
        "settlement_subtotal": 79.95,
        "settlement_subtotal_original": 79.95,
        "settlement_discount": 0.0,
        "settlement_tax": 0.0,
        "settlement_tax_original": 0.0,
        "settlement_total": 79.95,
        "settlement_total_original": 79.95,
        "fulfilled": true,
        "product": "https://api.comecero.com/api/v1/products/1001",
        "subscription": null,
        "shipments": "https://api.comecero.com/api/v1/orders/6622-NXKHE-858/items/1001/shipments",
        "license": null,
        "license_pending": true,
        "download": null,
        "meta": null
    "shipping_item": null,
    "customer": {
      "object": "customer",
      "url": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4",
      "customer_id": "0142130616328Cu6Vsk6M4",
      "date_created": "2017-01-04T00:43:36Z",
      "date_modified": "2017-01-04T00:45:09Z",
      "account_id": "AA0000",
      "test": true,
      "company_name": null,
      "name": "Joe Example",
      "email": "joe@example.com",
      "phone": null,
      "billing_address": {
        "object": "address",
        "name": "Joe Example",
        "address_1": null,
        "address_2": null,
        "city": null,
        "state_prov": "NY",
        "postal_code": "10023",
        "country": "US",
        "email": null,
        "meta": null
      "shipping_address": {
        "object": "address",
        "name": null,
        "address_1": null,
        "address_2": null,
        "city": null,
        "state_prov": null,
        "postal_code": null,
        "country": "US",
        "email": null,
        "meta": null
      "username": null,
      "tax_number": null,
      "tax_exempt": false,
      "has_payments": true,
      "locale": "en-US",
      "meta": null,
      "payment_methods": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/payment_methods",
      "payments": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/payments",
      "refunds": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/refunds",
      "orders": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/orders",
      "subscriptions": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/subscriptions",
      "invoices": "https://api.comecero.com/api/v1/customers/0142130616328Cu6Vsk6M4/invoices"
    "cart": "https://api.comecero.com/api/v1/carts/0142130616297CaItI3Jr5",
    "payment": "https://api.comecero.com/api/v1/payments/0142130709984PyZwyr0YD",
    "refunds": "https://api.comecero.com/api/v1/orders/6622-NXKHE-858/refunds",
    "invoice": null,
    "promotion": null,
    "shipments": "https://api.comecero.com/api/v1/orders/6622-NXKHE-858/shipments"

Response Payload Sample

<b>Username:</b> Joe Example<br>