# Mako API Integration Guide

The Mako Provisioning API (henceforth "Mako API") provides the ability for partners to securely automate the creation of new Makos, Companies and Users. This documentation describes the Mako API and how to use it within your systems for greater integration with the Mako CMS.

For more detailed information on Mako API endpoints refer to Mako API Specification, which contains a list of endpoints, data models, sample requests and responses.

Client implementations in Python and Java are also available as a reference.

## Prerequisites

Before using Mako API, you should have obtained API URL and credentials (client id and client secret) from Mako Networks. From now on, the URLs will be given in relative form to the base URL you received. You should be familiar with REST/JSON style API concepts.

## Authentication

Mako API authentication follows the standard OAuth 2.0 Bearer Token flow. To obtain an authentication token, perform a `POST to '/mako-api/auth/v1/auth' setting `client_id` and `client_secret` as form params:

POST /mako-api/auth/v1/auth?client_id=myclid&client_secret=mysecret

    Content-Type: application/x-www-form-urlencoded; charset=utf-8

Response [200 OK]

    {
      "access_token" : "YUMCABQAqX2cqBjSiFht",
      "token_type" : "Bearer",
      "expires_in" : 14400
    }

If you provide invalid credentials, you will get the following error response:

Response [401 Unauthorized]

    {
      "error" : "invalid_client"
    }

Next, when making a request to a protected resource (i.e. everything except `/auth` endpoint), include the bearer token in `Authorization` header:

GET /mako-api/config/v1/getCompany/

    Authorization: Bearer YUMCABQAqX2cqBjSiFht

Make sure you re-authenticate and obtain a new token before the old one expires.

If there is any problem with the authentication token (missing, invalid, expired, etc.), you will get the following error:

Response [401 Unauthorized]

    {
      "error" : "invalid_token"
    }

If your IP/Subnet is not authorized to access Mako API, you will get the following error:

Response [401 Unauthorized]

    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "unauthorizedSource",
        "message" : "Unauthorized source"
      }
    }

## Response envelope

Most Mako API responses are wrapped in a response envelope.[1] For example, when retrieving single mako information, the response will be the following:

GET /mako-api/config/v1/getMako/708680cf-07c5-4618-94be-3b12ddb92d84

Response [200 OK]

    {
      "status" : "OK",
      "body" : {
        "id" : "708680cf-07c5-4618-94be-3b12ddb92d84",
        "companyId" : "aa326232-c5f2-479e-8768-15430929497a",
        "name" : "LON 6500 M/LTE",
        "companyName" : "Some Company",
        "identification" : "11:11:11:22:33:44"
      }
    }

If the request was successful, the `body` field will contain the actual data of interest, such as Mako data, etc. In case of an error, you will receive a response similar to a one below:

Request

    POST /mako-api/config/v1/addMako

Response [200 OK]

    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "noDataFound",
        "message" : "Mako model not found"
      }
    }

Note that an erroneous response has an `error` field instead of `body`, where error code and message can be found. In case of e.g. validation errors, there might be more information returned in `errorDetails` field:

Response [200 OK]

    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "invalidData",
        "message" : "Invalid data supplied",
        "errorDetails" : {
          "violations" : [ {
            "path" : "mako.phoneNumber",
            "message" : "must not be null"
          }, {
            "path" : "mako.address.address1",
            "message" : "must not be null"
          }, {
            "path" : "mako.name",
            "message" : "must not be null"
          } ]
        }
      }
    }

To recap, in order to confirm that the request was successful, verify both HTTP status code to be 200, and the `status` field to be `OK`.

## Rate limiting

A per-user rate limit of 100 requests/sec is applied to all authenticated endpoints. The requests are counted per (user, endpoint), i.e. one user can perform max 100 requests/s to e.g. `GET /mako-api/v1/config/getMako` endpoint. If you exceed the rate limit, you will get the following response:

Response: [429 Too Many Requests]

    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "rateLimitExceeded",
        "message" : "Rate limit exceeded"
      }
    }

## Quotas

In addition to rate limiting, your requests are subject to daily quotas. Currently, quotas are applied to endpoints that create entities, e.g. `POST /mako-api/config/v1/addMako` etc., and you are limited to 10,0000 such requests per user per day. Should you exceed the quota limit, you will receive the following response:

Response [429 Too Many Requests]

    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "quotaLimitExceeded",
        "message" : "Quota limit exceeded"
      }
    }

Quota counters are reset every day at midnight (UTC).

## Error handling

This section contains typical errors you can encounter when working with Mako API - some of them were already mentioned in the previous sections.

When checking if the request was successful, make sure HTTP status is `200 OK` and wrapped response `status` field is `OK` (unless it’s `/auth` endpoint). Many errors (e.g. validation errors, invalid data, etc.) are returned with `200 OK` HTTP status code, so you have to account for that.

Some more high-level errors (e.g. method not allowed) do not return JSON, so your code should handle this. Below is the list of errors:

Error type | Status | Response | Comment
--- | --- | --- | ---
Invalid credentials | 401 |
    {
      "error" : "invalid_client"
    }
| Invalid credentials. Only returned by `/auth` endpoint.
Invalid token | 401 |
    {
      "error" : "invalid_token"
    }
| Invalid, missing, or expired authentication token
Unauthorized source | 401 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "unauthorizedSource",
        "message" : "Unauthorized source"
      }
    }
| Your IP/Subnet is not authorized to access Mako API
Not found | 404 |
    <html>
    <head><title>404 Not Found</title></head>
    <body>
    <h1>404 Not Found</h1>
    /mako-api/config/v1/aaddMako was not found on this server.
| Endpoint not found, check if you haven’t mistyped the URL
Method not allowed | 405 |
    <html>
    <head><title>Method not allowed</title></head>
| Wrong HTTP method used, e.g. PUT instead of POST etc.
Unsupported media type | 415 |
    <html>
    <head><title>415 Unsupported Media Type</title></head>
| Usually wrong `accept` header
Invalid parameter | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "invalidParam",
        "message" : "Invalid parameter value: id"
      }
    }
| Invalid parameter value (applies to path params, etc.)
Malformed JSON input | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "malformedJsonInput",
        "message" : "Malformed JSON input"
      }
    }
| Unparseable JSON sent.
Invalid data | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "invalidData",
        "message" : "Invalid value: mako.templateId"
      }
    }
| This is a general error type for invalid request data.
Invalid data - multiple violations | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "invalidData",
        "message" : "Request validation failed",
        "errorDetails" : {
          "violations" : [ {
            "path" : "mako.companyId",
            "message" : "Must not be null"
          },
          {
            "path" : "mako.address.address1",
            "message" : "Must not be null"
          }
         ]
        }
      }
    }
| Invalid request data - response contains multiple violations
Internal server error | 500 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "internalServerError",
        "message" : "Unexpected server error"
      }
    }
| Internal server error occurred. Contact Mako Networks.
Rate limit exceeded | 429 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "rateLimitExceeded",
        "message" : "Rate limit exceeded"
      }
    }
| You are performing requests too fast.
Quota limit exceeded | 429 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "quotaLimitExceeded",
        "message" : "Quota limit exceeded"
      }
    }
| You have run out of quota for the given endpoint. You need to wait until the next day (UTC) to perform more requests to this endpoint.
Quota limit exceeded | 429 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "quotaLimitExceeded",
        "message" : "Quota limit exceeded"
      }
    }
| You have run out of quota for the given endpoint. You need to wait until the next day (UTC) to perform more requests to this endpoint.
General Error | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "generalError",
        "message" : "Cannot delete yourself"
      }
    }
| An umbrella error for various client-side error conditions. Please see the message to find out the reason for error.
No data found | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "noDataFound",
        "message" : "Mako not found"
      }
    }
| Returned when trying to retrieve non-existent data, e.g. when wrong Mako ID was provided etc.
Invalid state | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "invalidState",
        "message" : "Company Already Deleted"
      }
    }
| Returned when trying to perform an operation on an entity that’s in invalid state for the operation, e.g. deleting an already deleted Mako, etc.
Duplicate entity | 200 |
    {
      "status" : "FAILED",
      "error" : {
        "errorCode" : "duplicateEntity",
        "message" : "Mako identification already in use"
      }
    }
| Returned when some entity is already in use (e.g. trying to use Mako identification that was already used for some other Mako).

## Example workflow

Here’s an example workflow that demonstrates how to add a new company, and how to add a person, and a Mako for the newly created company. (authentication and HTTP status codes omitted for brevity)

First, let’s create a new company. You need to know the UUID of the parent company under which the new company will be created. (you may retrieve the list of companies using `/listCompanies` endpoint)

POST /mako-api/config/v1/addCompany

    {
      "parentCompanyId" : "5f10c94d-af26-479f-a705-aa2a85d778ea",
      "companyType" : "RESELLER",
      "name" : "Acme",
      "address" : {
        "address1" : "3205  Sycamore Fork Road",
        "suburb" : "Suburb",
        "city" : "Fort Lauderdale",
        "state" : "Florida",
        "postOrZipCode" : "33308",
        "country" : "US"
      },
      "phoneNumber" : "9542177110",
      "supportEmail" : "test3@makonetworks.com",
      "salesEmail" : "test2@makonetworks.com",
      "generalEmail" : "test1@makonetworks.com"
    }

Response [200 OK]

    {
      "status" : "OK",
      "body" : {
        "id" : "e18ceab1-9071-41cc-a6dd-1e5bc2d5d962",
        "name" : "Acme"
      }
    }

The response contains the unique id of the newly created company. Use this id to refer to the company in your future requests.

Now, we’re going to add a new person to the newly created company:

POST /mako-api/config/v1/addPerson

    {
      "companyId" : "f8a3ae1a-967a-4032-a053-e012cfe34537",
      "userType" : "Full Reseller",
      "templateScope" : "COMPANY_NO_INHERIT",
      "firstName" : "Jane",
      "lastName" : "Smith",
      "username" : "acmereseller12",
      "email" : "acmeresseler1@makonetworks.com",
      "allowLogin" : true,
      "address" : {
        "address1" : "1355",
        "address2" : "N McLean Blvd",
        "city" : "Elgin",
        "state" : "Illinois",
        "postOrZipCode" : "60123",
        "country" : "US",
        "timezone" : "America/Chicago"
      },
      "phoneNumber" : "123423423"
    }

Response [200 OK]

    {
      "status" : "OK",
      "body" : {
        "id" : "f2034c04-be11-43b0-b39c-2535cd273661",
        "firstName" : "Jane",
        "lastName" : "Smith",
        "username" : "acmereseller12",
        "email" : "acmeresseler1@makonetworks.com"
      }
    }

Again, note the returned `id` for the created person.

Finally, let’s add a Mako to the newly created company:

POST /mako-api/config/v1/addMako

    {
      "companyId" : "f8a3ae1a-967a-4032-a053-e012cfe34537",
      "name" : "Test Mako",
      "address" : {
        "address1" : "1355",
        "address2" : "N McLean Blvd",
        "suburb" : "Suburb",
        "city" : "Elgin",
        "state" : "Illinois",
        "postOrZipCode" : "60123",
        "country" : "US",
        "timezone" : "America/Chicago",
        "latitude" : 42.06,
        "longitude" : -88.31
      },
      "phoneNumber" : "+18008514690",
      "faxNumber" : "+18008514691",
      "makoModel" : "Mako 6500-M"
    }

Response [200 OK]

    {
      "status" : "OK",
      "body" : {
        "id" : "7db65f69-ed6e-4b23-bf3f-2063b41ccdd1",
        "companyId" : "f8a3ae1a-967a-4032-a053-e012cfe34537",
        "name" : "Test Mako",
        "companyName" : "Acme3",
        "identification" : "MAKO-ID-HERE-5721-1587738483248"
      }
    }

When creating a Mako, a Mako model (hardware name), you can provide identification (Mac address), or you can create a mako using template (another Mako). Refer to API specification for details.

1. Except `/auth` endpoint responses and some error responses such as JSON parse errors