Skip to main content
The first thing to do is acquire access credentials from CRO software. Please review the Authentication Guide for more information.

Base URL

https://api.crosoftware.com/v0
All endpoint paths in this documentation are relative to this base URL. If you have integration code or examples referencing an older crosoftware.net-based URL, update it to the value above: the production API and its base domain migrated to api.crosoftware.com.

Tokens, Scopes, Users, & Passwords

An access token is used to authenticate your software to the CRO Developer API. The user and password are used to authenticate users to your software. Scopes determine what functionality your software will have access to. See the Authentication Guide for more on how to authenticate your applications.

Errors

All errors are handled similarly. The HTTP response status line will describe what happened (human readable):
400 CreateCustomerModel.user_id: Unknown field.
…and the body will contain a structured response:
{
  "field": "CreateCustomerModel.user_id",
  "detail": "Unknown field.",
  "explanation": "The resource could not be accessed."
}
In this example, you could refer to the CreateCustomerModel schema in the API Reference for more information about the expected fields. Input errors will contain a model name and/or field names that can be referenced in the documentation. The ‘detail’ field of the error schema will contain the reason the request was rejected.

Request Data Formats

Currently only JSON is supported.

Response Data Formats

Currently only JSON is supported. Some endpoints may return reports in other formats; refer to each endpoint’s documentation for details.

Roles

The access token(s) you’ll be issued grant you access to the API. The scope(s) granted to each token determine which API functions you can access. The CRO user you log in with determines which role(s) are assigned. The roles the user is assigned determine which endpoints are accessible and also what data is returned by each endpoint. Refer to each endpoint’s documentation for details on which roles have access and what data will be returned.

Available Roles

Public
Admin
Driver
Dispatcher
CrmUser
ClientCustomer
ThirdPartyDispatcher
ThirdPartyDriver
ThirdPartyDispatcher and ThirdPartyDriver are a separate, parallel hierarchy from the first-party roles above, not a nested extension of them. See the FAQ for details.

Sample Script: Export Customers

In this sample we’ll export customers using the CRO Developer API. Refer to the code below. Replace ‘your-token-here’ and ‘your-tenant-id-here’ with the values issued by CRO Software. If you’re not sure what these values should be, contact us at crodev@rapidworks.com.
cURL
curl -X GET 'https://api.crosoftware.com/v0/customers' \
  -H 'Authorization: bearer <your-token-here>' \
  -H 'x-tenant-id: <your-tenant-id-here>' \
  -H 'content-type: application/json'
import requests

url = 'https://api.crosoftware.com/v0/customers'
headers = {
    'Authorization': 'bearer <your-token-here>',
    'x-tenant-id': '<your-tenant-id-here>',
    'content-type': 'application/json'
}
params = {}
response = requests.get(url, headers=headers, params=params)
print(response.text)
const url = 'https://api.crosoftware.com/v0/customers';
const headers = {
  'Authorization': 'bearer <your-token-here>',
  'x-tenant-id': '<your-tenant-id-here>',
  'content-type': 'application/json'
};

const response = await fetch(url, { headers });
const data = await response.json();
console.log(data);
This uses the requests library for Python; the JavaScript sample uses the built-in fetch (Node.js 18+ or a browser).

Response

Note the response from /customers is a paged result set. All endpoints in the API return lists as paged responses with this format. In the next section we’ll iterate the results and print them to the console.
{
  "current_page": 1,
  "current_limit": 100,
  "total_pages": 6,
  "total_count": 504,
  "results": [
    {
      "addresses": [
        {
          "is_active": true,
          "is_billing": true,
          "is_physical": true,
          "is_shipping": true,
          "country": "US",
          "id": 1,
          "latitude": 54,
          "line_1": "123 Some St.",
          "line_2": null,
          "line_3": null,
          "line_4": null,
          "locality": "Sequim",
          "longitude": 128,
          "postcode": "98368",
          "region": "WA"
        }
      ],
      "contacts": [
        {
          "notify_on_acknowledged_request": false,
          "notify_on_completed_request": false,
          "notify_on_dispatched_request": false,
          "notify_on_failed_request": false,
          "notify_on_new_request": false,
          "email": "test@contacts.com",
          "fax": null,
          "id": 1,
          "name": "Sarah Jane",
          "number": null
        }
      ],
      "created_on": "2020-10-19T04:06:50.733000",
      "last_updated_on": "2020-10-19T04:06:50.733000",
      "name": "Customer A",
      "parent_id": null,
      "customer_id": 1,
      "is_active": true,
      "is_commercial": false,
      "location_id": 1,
      "note": null,
      "reference_number": "1234",
      "renewal_date": null,
      "sales_rep": "A Sales Guy",
      "sales_rep_id": null,
      "suspension_id": null
    },
    ...
  ]
}

Paging

The fields of a paged result set have the same basic schema:
{
  "current_page": 1,
  "current_limit": 100,
  "total_pages": 6,
  "total_count": 504,
  "results": ...
}
To iterate through all customers, you need to write the code to request the pages in a loop:
cURL
url='https://api.crosoftware.com/v0/customers'
page=1

while :; do
  response=$(curl -s "$url?page_index=$page" \
    -H 'Authorization: bearer <your-token-here>' \
    -H 'x-tenant-id: <your-tenant-id-here>' \
    -H 'content-type: application/json')

  echo "$response" | jq -c '.results[]'

  total_pages=$(echo "$response" | jq '.total_pages')
  page=$(( $(echo "$response" | jq '.current_page') + 1 ))
  [ "$page" -gt "$total_pages" ] && break
done
import requests, json


def process_customers(cb, **kw):
    url = 'https://api.crosoftware.com/v0/customers'
    headers = {
        'Authorization': 'bearer <your-token-here>',
        'x-tenant-id': '<your-tenant-id-here>',
        'content-type': 'application/json'
    }
    next_page = 1
    while True:
        params = {'page_index': next_page}
        response = requests.get(url, headers=headers, params=params)
        results = json.loads(response.text)
        for item in results['results']:
            cb(item, **kw)
        total_pages = int(results['total_pages'])
        next_page = int(results['current_page']) + 1
        if next_page > total_pages:
            break


process_customers(print)
const url = 'https://api.crosoftware.com/v0/customers';
const headers = {
  'Authorization': 'bearer <your-token-here>',
  'x-tenant-id': '<your-tenant-id-here>',
  'content-type': 'application/json'
};

async function processCustomers(cb) {
  let nextPage = 1;
  let totalPages;

  do {
    const response = await fetch(`${url}?page_index=${nextPage}`, { headers });
    const results = await response.json();
    results.results.forEach(cb);
    totalPages = results.total_pages;
    nextPage = results.current_page + 1;
  } while (nextPage <= totalPages);
}

processCustomers(console.log);
The cURL sample uses jq to parse the JSON response between requests.