NAV Navbar
php NodeJS
  • Introduction
  • Authentication
  • General Usage
  • API Endpoints:
  • Appointments
  • Campaigns
  • Campaigns Categories
  • Categories
  • Coupon Validation
  • Employees
  • Invoices
  • Locations
  • Postal Codes
  • Reports
  • Reviews
  • Tax Rates
  • Push API
  • Release Notes
  • To-Be-Documented
  • Introduction

    Welcome to the Scheduling Center API! You can use our API to access a number of Scheduling Center functions, from invoicing to scheduling and more.

    Current Version: v1.6.3 Short Stack

    Authentication

    While normally the Scheduling Center uses cookies to store authentication (and optionally you can continue to do so at your discretion), most features can also be accessed statelessly with Basic Authentication.

    All of the API endpoints listed in this document can be accessed using this authentication protocol as long as you prefix the URL with /api/.

    For example:

    https://crm.oxifresh.com/api/reviews.json

    Without /api/, you'll be limited to cookie-based authentication with a standard usernaame/password combo you first POST to:

    https://crm.oxifresh.com/employees/login

    API Keys

    To authenticate, use this code:

    <?php
    /**
     * To use Basic Authentication in PHP, include CURLOPT_USERPWD in all curl requests. For example: 
     */
    
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/api/reviews.json');
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if ( ! in_array($info['http_code'], array('200', '201', '204'))) {
        // Success if a 200 code
    }
    ?>
    
    /**
     * Our examples will be using the request library with the following defaults:
     * See https://www.npmjs.com/package/request 
     */
    var request = require('request') 
    
    // We'll also have set up these defaults:
    baseRequest = request.defaults({
      json: true,                             // Examples will be sending object data in JSON format
      headers: {
        'X-Requested-With': 'XMLHttpRequest'  // This will signal you want AJAX-y responses instead of HTML
      },
      auth:{                                  // To use Basic authentication, with the /api endpoints, list your username and API Key:
        user: 'api_username',
        password: 'api_key'
      }
    })
    
    
    // Example request:
    var data = {franchsie_id: 2}
    baseRequest({
      url: "https://192.168.50.4/api/categories.json?",
      method: "GET",
      body: data
    },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
          console.log(body)
        }
      }
    )
    
    
    
    /**
     * To use Basic Authentication with jQuery, include beforeSend in all requests. For example: 
     */
    
    jQuery.ajax({
      url: '/reviews.json',
      success: function (obj) {
          // Resutls are in obj.review .. 
        },
        beforeSend: function (xhr) {
            xhr.setRequestHeader ("Authorization", "Basic " + btoa(api_username + ":" + api_password));
        },
    });
    

    Since Basic Authentication requests are set in plain-text, we only allow the use of API Keys (in place of user's normal passwords). For the same reason, we only support HTTPS requests.

    You will need to be given an API username & API Key by a system admin to begin.

    Basic Authentication PHP

    Per PHP curl documentation this is specified as a colon (:) separated string:

    curl_setopt($curl_handle, CURLOPT_USERPWD, 'api_username:api_password');

    NodeJS

    A number of libraries are available to assist with sending HTTP requests. In this documentation our examples will be using the request library. The auth config option will send Basic authentication headers with the request:

    auth:{ user: 'api_username', password: 'api_key' }

    In the Browser

    And in jQuery can be listed in the beforeSend function, setting the Authorization header using btoa:

    xhr.setRequestHeader ("Authorization", "Basic " + btoa(api_username + ":" + api_password));

    General Usage

    Routing

    We've updated our application to follow more common industry standard URL routes, which rely on HTTP Methods to determine what action to take.

    In general all controllers will accept at least GET, POST, PATCH, PUT and DELETE HTTP methods.

    For example, the reviews endpoint will respond to the following methods:

    HTTP Method Example Description
    GET /reviews Returns a list of reviews
    GET /reviews/:review_id Returns details on a single review by ID
    POST /reviews Creates a new Review (with the review data passed in the request body)
    PUT or PATCH /reviews/:review_id Updates a review (all fields passed are updated, any left out are left as-is)
    DELETE /reviews/:review_id Deletes a single review by ID

    The API may support custom actions, if so they'll follow similar HTTP-method conventions, ex:

    HTTP Method Example Description
    POST /servicesSettings/copyCorporate/:corporate_service_id Duplicates a Corporate service by ID down to a specific Franchisee
    DELETE /service/fullDelete/:service_id Hard-deletes a Corporate service by ID

    Old Route Style (Legacy Support)

    For now, we'll continue to support the legacy routes, which required you state the action in the URL explicitly.

    Here they are for reference

    1. index
      • This is the default action when none is specified
      • Ex. `https://crm.oxifresh.com/api/reviews/index -or - https://crm.oxifresh.com/api/reviews
      • GET-request only
    2. view
      • Must specify the ID for the action in the URL
      • Ex. - https://crm.oxifresh.com/api/reviews/view/1
      • GET-request only
    3. add
      • Must specify data for the new entity in POST
      • https://crm.oxifresh.com/api/reviews/add
      • POST-requests only
    4. edit
      • Must specify the ID for the entity in the URL, and the data for the new entity in POST
      • Ex/ https://crm.oxifresh.com/api/reviews/edit/1
      • POST, PUT, or PATCH-requests only
    5. delete
      • Must specify the ID of the entity in the URL
      • https://crm.oxifresh.com/api/reviews/delete/1
      • DELETE-request only

    Certain controllers may implement additional functions as specified in the docs.

    Franchise/Corporate Access

    There are two general types of users in our application: 1. corporate users, which have general access to all Franchise accounts, and 2. franchisees users, which only have access to records for a single Franchise

    Most likely your account was issued as a corporate user, which has implicit access to all our Franchise accounts. By default, list views will show records for all franchise accounts. To limit records to a single franchise, specify a franchise_id in the request.

    When using Basic Authentication, since you do not have a session between requests, you'll need to always specify a Franchise ID in the index request to limit results to just that account.

    However when using cookie-based authentication methods, the Franchise ID is stored in your session and remembered between requests. To reset it back to the 'all franchise' lists, pass franchsie_id=0 in your request.

    Output Formats

    For simplicity, all requests prefixed with /api/ will return JSON-formatted output.

    However you can request several custom formats, either by specifying the type in the Accept: header:

     baseRequest({
         url: "https://crm.oxifresh.com/api/invoices",
         method: "GET",
         headers: {
           'Accept': 'application/xml'
         }
       }
     )
    
    

    ... or by appending an extension to the end of the URL, eg:

    https://crm.oxifresh.com/api/reviews/1.xml - Will return XML

    https://crm.oxifresh.com/api/reviews/1.json - Will return JSON

    The API provides output in three formats:

    1. JSON
      • Set the Request Header Accetps: application/json, or append the request with .json
    2. XML
      • Set the Request Header Accetps: application/xml, or append the request with .xml
    3. CSV (note this has limited support)
      • Set the Request Header Accetps: application/csv, or append the request with .csv
    4. HTML * Omit the /api/ prefix. Note that you'll have to use Cookie-based Authentication to get the HTML form versions.

    Universal Parameters

    For all listing requests (eg. GET /reviews) , the following parameters are always accepted. For brevity, we won't list them in the rest of the documentation:

    Parameter Default Description
    franchise_id null Restricts the results to the Franchise account ID specified. To pull records for all franchises, use ID 0.
    page 1 Results are paginated, and the default page is 1 (the first set of data). Increment to view the next "page" of entities
    limit 10 The default limit of the paginated results is 10

    Global Results

    All listing requests (eg. GET /reviews) will return the following parameters at the end of the results, as results are paginated. For brevity, we won't list them in the rest of the documentation:

    Parameter Description
    total The total number of entities matching the input parameters
    page The current page number of the results
    limit How many of the total results were returned for this page

    Rate Limits

    You will be limited to 3000 requests per hour.

    At this time we also ask that you run extraordinarily heavy tasks before 6 AM MST and after 8 PM MST and to ensure an unusual increase in load does not adversely affect our day to day operations.

    If you require more frequent updates throughout the day, you may want to check out our Push API, which can forward changes to your application in near-real-time.

    Filtering Fields

    To filter the fields and associations for Invoices down to just totals and names, for example:

    <?php
    $curl                 = curl_init();
    
    // Prep data for the GET request
    $data = [
        'franchise_id' => 2,
        'fields' => json_encode([
            "invoices" => ["id", "date_completed", "amount_due"],
            "contacts" => ["id", "first_name", "last_name"]])
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/invoices.json?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {
      franchsie_id: 2,
      fields: JSON.stringify({
        invoice:  ["id", "date_completed", "amount_due"],
        contacts: ["id", "first_name", "last_name"]
      })
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/invoices.json",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return only the 3 requested fields on Invoices, plus the first and last name for it’s Contact (see sidebar).

    {
        "invoices": [
            {
                "id": 910822,
                "date_completed": "2017-11-21T15:30:00+00:00",
                "amount_due": 125,
                "contact_id": 930946,
                "contact": {
                    "id": 930946,
                    "first_name": "John ",
                    "last_name": "Smith"
                }
            }
        ],
        "total": 493,
        "page": 1,
        "limit": 1,
        "fields": {
            "Invoices": [
                "Invoices.id",
                "Invoices.date_completed",
                "Invoices.amount_due",
                "contact_id"
            ],
            "Contacts": [
                "id",
                "first_name",
                "last_name"
            ]
        },
        "matching": {
            "InvoicesCampaigns.Campaigns.code": "CLEAN18"
        }
    }
    

    By default, API endpoints will include all of the fields listed on the entity, as well as a list of commonly useful associated entities and all of their fields. While this can be useful data, it can also be including a lot of information you don't necessarily need for your specific integration.

    The Invoices index endpoint, for example, will by default return all the fields for the invoice, plus the associated Invoice Campaigns and their Campaigns fields, Contact information, and Address information. It also contains a couple of “virtual” fields, which are derived values not stored on the entity directly but computed on the fly. For Invoices, the Zone field and the boolean “is_completed” fields are “virtual” fields computed on request.

    While this data could be useful, it also can slow down response times significantly at scale. The underlying query is rather large (requiring at least 4 joins and several sub-queries for loading associations & virtual field values), and computing virtual field values is particularly slow at scale.

    Examples

    If you need to request large sets of data, it can dramatically increase response times if you specify only the list of fields you actually need. This can help eliminate superfluous associations and shrink the size of the response payload significantly. To slim down the list of fields, simply include a fields parameter in your request, containing a JSON-encoded list of the entity fields & associations fields you need.

    Note that it now also includes the list of “fields” that matched in your request. If a requested field doesn’t show in this list, double check that it’s valid and also that it’s not a “virtual” field (these are not supported at this time). Also ensure that when specifying association’s fields that you use the base entity table name, which is generally plural regardless of the type of association. In the above example, Invoices have only a single potential Contact, so that’s returned as “contact” in the API data. However the base table is plural, so in the fields list you must specify it as “contacts” (plural).

    Also note that the “matching” node is now added when filtering by campaigns code, to help reduce confusion as the dataset may not actually include the invoice campaign data if not specified in the “fields” parameter, even though it was used to filter the results.

    Limitations

    Note also that at this time it can only support filtering associations one level deep, so it's not possible to select fields for an Invoice's -> Invoice Campaign's -> Campaign.

    Wild Cards

    You may use wildcards to select all fields for a specific association, for example to select all the Contact's field use an asterisk:

    https://crm.oxifresh.com/invoices.json?campaign_code=CLEAN18&limit=1&page=1&fields={"invoices":["id","date_completed","amount_due"],"contacts":["*"]}
    

    API Endpoints:

    Below are the details for each public endpoint, listed alphabetically.

    Note that a fair number of endpoints are still undocumented but otherwise fully functional. Check out the To-Be-Documented section at the ned for more information.

    Appointments

    Currently the Appointments API has a limited set of functions - you can currently pull lists of appointments, or get detailed extended data about single appointments. Adding/editing appointments is possible, but must be done in a non-standard form-submission method that exactly mimics the current Calendar front-end in the real app (it is not recommended for external use at this time).

    If you need to book appointments, we can review options for you as there are several possible approaches. A simplified API for booking new appointments (and managing jobs in general) is planned for a future release TBD.

    List All Appointments

    <?php
    
    $curl                 = curl_init();
    
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/appointments?franchise_id=501');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    ?>
    
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/appointments",
        method: "GET",
        accepts: "application/json",
        body: {
          'franchise_id' : 501,
        }
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "appointments": [
            {
                "id": 2,
                "type_id": 1,
                "franchise_id": 501,
                "contact_id": 123456,
                "invoice_id": 55421,
                "created_by_id": 1,
                "modified_by_id": 1,
                "employee_id": 3020,
                "start_datetime": "2018-08-27T11:15:00+00:00",
                "end_datetime": "2018-08-27T12:35:00+00:00",
                "vehicle_id": null,
                "notes": "Gate code 456",
                "date_created": "2018-08-27T17:03:34+00:00",
                "last_modified": "2018-08-27T17:03:34+00:00",
                "cancelled": false,
                "cancellation_reason": null,
                "cancellation_message": null,
                "cancelled_by_id": null,
                "date_cancelled": null,
                "parked": false,
                "parked_by_employee_id": null,
                "parent_appointment_id": null,
                "address_id": 577005,
                "appointments_booking_method_id": 1,
                "address": {
                    "id": 577005,
                    "entity_type": 4,
                    "entity_id": 566031,
                    "template_id": 1,
                    "type_id": 1,
                    "template_group_id": null,
                    "address1": "123 Fake St",
                    "address2": "Building 2",
                    "unit_type": 1,
                    "unit_number": "23",
                    "county": null,
                    "city": "Lakewood",
                    "state": "CO",
                    "zipcode": "80228",
                    "country_id": 1,
                    "active": 1,
                    "geocode": "39.716442,-105.134725",
                    "latitude": null,
                    "longitude": null,
                    "unit_type_name": null,
                    "full_street": "123 Fake St Building 2",
                    "country_code": "US",
                    "type_name": "Service"
                },
                "appointments_type": {
                    "id": 1,
                    "name": "Job Scheduled",
                    "short_name": null,
                    "lookupKey": null,
                    "duration": 3600,
                    "isJobType": true,
                    "active": true
                }
            }
        ]
     }   
    

    Lists all appointment information, including any address, and the type of appointment.

    Filtering & Date Limiting

    Similar to the Invoices endpoint, whenever an appointment is updated, it's last_modified field is updated. You can limit results to those updated after a specific date with the last_modified_after parameter.

    You can also reduce the size of the response by using the standard filtering methods outlined in the general Filtering Fields docs.

    Bulk Listings

    The default page size is just 10 records, but you can increase that limit significantly with limit.

    If the limit is too high, requests can timeout or memory overflow and the API will return a 500-coded response.

    HTTP Request

    GET http://crm.oxifresh.com/appointments

    Response

    A list of Appointment entities with the following fields:

    Parameter Description
    type_id A reference to the AppointmentsType (1 for a Job, 2 Estimate, 5 Re-do, 6 Other, 7 Marketing, 9 Pull-job, 9 Online Reservation)
    franchise_id The Franchise ID the appointment is associated with
    contact_id The ID of the customer the appointment is for
    invoice_id A linked Invoice, if available
    created_by_id The Employee ID of who created the appointment
    modified_by_id The Employee ID of the last person to modify the appointment
    employee_id The Employee ID of the person assigned to go to the Appointment
    start_datetime The Franchisee-local datetime the appointment starts at
    end_datetime The Franchisee-local datetime the appointment ends at
    vehicle_id DEPRECATED - The Vehicle ID assigned to the job
    notes Any comments added manually durring booking
    date_created UTC datetime the appointment was created
    last_modified UTC datetime the appointment was last modified
    cancelled Boolean true/false if the appointment is cancelled
    cancellation_reason A CancellationReason ID (Deprecated - all of them will be 13 as of now)
    cancellation_message Comments manually entered about why the job was cancelled
    cancelled_by_id The Employee ID of the person who cancelled it
    date_cancelled The UTC datetime the job was cancelled
    parked Boolean true/false if the job has been "parked" in preparation for rescheduling it to another day/time
    parked_by_employee_id The Employee ID of who parked it last
    parent_appointment_id If associated to a parent Appointment (Pull jobs usually are), that ID
    address_id The Address ID of the appointment location
    appointments_booking_method_id The Appointment Booking Method ID that indicates how it was booked (1 for Call Center, 2 Online Scheduler, 3 Chat Bot)

    Campaigns

    Marketing activities are grouped into Campaigns, which are single-use, optionally date-ranged records of a marketing spend or activity.

    They all specify a Code, which might be printed on a physical coupon pack, or posted online, to use for easy lookup on the phone or when booking online.

    They also can optionally list a fixed cost - used for ROI (return on investment) calculations in reports.

    Campaigns historically would list a set of Coupons under each record, however they now can instead specify a pre-built, re-usable Coupon Pack instead, to share coupons between multiple Campaigns.

    Get a list of campaigns

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 2
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    ?>
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaigns": [
            {
                "id": 1,
                "franchise_id": 2,
                "coupons_pack_id": null,
                "campaigns_category_id": 10,
                "campaigns_subcategory_id": 1,
                "start_date": "2018-01-01T00:00:00+00:00",
                "end_date": "2018-03-01T00:00:00+00:00",
                "name": "January Valpak 2018",
                "code": "VP0110",
                "description": "Mailed to 320,000 households on the second week of the month",
                "cost": 5785,
                "last_modified": "2016-03-09T12:29:51+00:00",
                "date_created": null,
                "hidden": false,
                "active": false,
                "coupons": [
                    {
                        "id": 1,
                        "franchise_id": 2,
                        "coupons_pack_id": null,
                        "campaign_id": 1,
                        "name": "Whole House Carpet Cleaning",
                        "description": "6 Room Special $168",
                        "start_date": "2018-01-25T00:00:00+00:00",
                        "expiration_date": "2018-03-01T00:00:00+00:00",
                        "created_by_employee_id": 0,
                        "last_modified_by_employee_id": 0,
                        "date_created": "2018-01-25T18:24:22+00:00",
                        "last_modified": "2014-04-09T15:39:38+00:00",
                        "cannot_be_combined": false,
                        "allow_total_under_minimum": false,
                        "list_order": 0,
                        "active": false,
                        "expired": true
                    },
                    {
                        "id": 2,
                        "franchise_id": 2,
                        "coupons_pack_id": null,
                        "campaign_id": 1,
                        "name": "2 Room Special",
                        "description": "2 Rooms Cleaned $68",
                        "start_date": "2018-01-25T00:00:00+00:00",
                        "expiration_date": "2008-11-30T00:00:00+00:00",
                        "created_by_employee_id": 0,
                        "last_modified_by_employee_id": 0,
                        "date_created": "2018-01-25T18:41:25+00:00",
                        "last_modified": "2014-04-09T15:39:38+00:00",
                        "cannot_be_combined": false,
                        "allow_total_under_minimum": false,
                        "list_order": 0,
                        "active": false,
                        "expired": true
                    }
                ],
                "coupons_pack": null,
                "campaigns_subcategory": {
                    "id": 1,
                    "franchise_id": 2,
                    "campaigns_category_id": 10,
                    "name": "ValPak",
                    "unknown_default": false,
                    "active": true,
                    "hidden": 0
                },
                "campaigns_category": {
                    "id": 10,
                    "icon": "envelope-o",
                    "name": "Shared Mail",
                    "abbreviation": null,
                    "campaigns_categories_cost_type_id": 1,
                    "display_order": 3,
                    "reportable": true,
                    "active": true
                }
            },
            {
                "id": 39710,
                "franchise_id": 2,
                "coupons_pack_id": 2,
                "campaigns_category_id": 7,
                "campaigns_subcategory_id": 16413,
                "start_date": "2018-01-22T00:00:00+00:00",
                "end_date": null,
                "name": "Online PPC",
                "code": "PPC0114",
                "description": "",
                "cost": 4210,
                "last_modified": "2018-01-22T16:55:55+00:00",
                "date_created": "2018-01-22T16:12:03+00:00",
                "hidden": false,
                "active": false,
                "coupons": [],
                "coupons_pack": {
                    "id": 2,
                    "name": "Default Coupon Pack",
                    "franchise_id": 2,
                    "date_created": "2017-10-10T16:35:50+00:00",
                    "last_modified": "2017-11-14T10:53:08+00:00",
                    "created_by_employee_id": 1,
                    "last_modified_by_employee_id": 1,
                    "is_default": true,
                    "active": true,
                    "coupons": [
                        {
                            "id": 83353,
                            "franchise_id": 2,
                            "coupons_pack_id": 2,
                            "campaign_id": 25257,
                            "name": "2 Room Special ",
                            "description": "2 Rooms for $68",
                            "start_date": "2015-08-05T00:00:00+00:00",
                            "expiration_date": null,
                            "created_by_employee_id": 0,
                            "last_modified_by_employee_id": 598,
                            "date_created": "2015-08-05T14:55:00+00:00",
                            "last_modified": "2017-12-31T07:26:01+00:00",
                            "cannot_be_combined": false,
                            "allow_total_under_minimum": false,
                            "list_order": 0,
                            "active": false,
                            "expired": false
                        },
                        {
                            "id": 96738,
                            "franchise_id": 2,
                            "coupons_pack_id": 2,
                            "campaign_id": null,
                            "name": "Three Rooms Special ",
                            "description": "3 Rooms Cleaned $70",
                            "start_date": "2016-08-02T00:00:00+00:00",
                            "expiration_date": null,
                            "created_by_employee_id": 0,
                            "last_modified_by_employee_id": 1,
                            "date_created": "2016-08-02T15:44:53+00:00",
                            "last_modified": "2017-11-14T03:53:29+00:00",
                            "cannot_be_combined": true,
                            "allow_total_under_minimum": false,
                            "list_order": 0,
                            "active": true,
                            "expired": false
                        }
                    ]
                },
                "campaigns_subcategory": {
                    "id": 16413,
                    "franchise_id": 2,
                    "campaigns_category_id": 7,
                    "name": "Test Subcategory 2 - The Retesting",
                    "unknown_default": false,
                    "active": false,
                    "hidden": 0
                },
                "campaigns_category": {
                    "id": 7,
                    "icon": "wifi",
                    "name": "Radio",
                    "abbreviation": null,
                    "campaigns_categories_cost_type_id": 1,
                    "display_order": 10,
                    "reportable": true,
                    "active": true
                }
            }
        ]
    }
    

    This endpoint gets a paginated list of Campaigns, along with their parent Category & Subcategory, and Coupons.

    Note that older Campaigns will list a set of unique Coupons, while newer Campaigns will be linked to a Coupon Pack by specifying a coupons_pack_id. The details for the shared Coupon Pack & it's Coupons are included in the results instead.

    HTTP Request

    GET http://crm.oxifresh.com/campaigns

    Query Parameters

    Parameter Default Description
    code null A specific campaign code to match against
    campaigns_subcategory_id null Restricts list to those that match the specified Campaigns Subcategory ID

    Response

    Parameter Description
    campaigns A paginated list of campaigns, includes a count of the number of active Coupons listed for this Campaign.
    total The total number of campaigns matching the input criteria

    Get a Specific Campaign

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns/1');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns/1",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaign": {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2017-01-01T00:00:00+00:00",
            "end_date": "2017-03-01T00:00:00+00:00",
            "name": "January Valpak 2018",
            "code": "VP0117",
            "description": "Mailed to 320,000 households on the second week of the month",
            "cost": 5785,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": "2016-03-09T19:29:51+00:00",
            "hidden": false,
            "active": true,
            "campaigns_subcategory": {
                "id": 1,
                "franchise_id": 2,
                "campaigns_category_id": 10,
                "name": "ValPak",
                "active": true,
                "hidden": 0
            },
            "campaigns_category": {
                "id": 10,
                "icon": "envelope-o",
                "name": "Shared Mail",
                "campaigns_categories_cost_type_id": 1,
                "display_order": 3,
                "active": true
            }
        }
    }
    

    This endpoint retrieves a specific campaign by ID.

    HTTP Request

    GET http://crm.oxifresh.com/campaigns/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign to retrieve

    Response

    Parameter Description
    campaign The campaign data

    Add A Campaign

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'name' => 9,
        'franchise_id' => 2,
        'start_date' => '2017-05-01',
        'campaigns_category_id' => 10,
        'campaigns_subcategory_id' => 1,
        'code' => 'TESTCODE',
        'description' => 'Test campaign'
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    var data = {
        'name' :  9,
        'franchise_id' :  2,
        'start_date' :  '2017-05-01',
        'campaigns_category_id' :  10,
        'campaigns_subcategory_id' :  1,
        'code' :  'TESTCODE',
        'description' :  'Test campaign'
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns",
        method: "POST",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaign": {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2017-05-01T00:00:00+00:00",
            "end_date": "",
            "name": "Test Campaign",
            "code": "TESTCODE",
            "description": "Test campaign",
            "cost": 0,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": "2016-03-09T19:29:51+00:00",
            "hidden": false,
            "active": true,
            "campaigns_subcategory": {
                "id": 1,
                "franchise_id": 2,
                "campaigns_category_id": 10,
                "name": "ValPak",
                "active": true,
                "hidden": 0
            },
            "campaigns_category": {
                "id": 10,
                "icon": "envelope-o",
                "name": "Shared Mail",
                "campaigns_categories_cost_type_id": 1,
                "display_order": 3,
                "active": true
            }
        }
    }
    

    This endpoint adds a new campaign to the system.

    HTTP Request

    POST http://crm.oxifresh.com/campaigns/<ID>

    Data Parameters

    Parameter Optional Default Description
    franchise_id No null The franchise ID this belongs in
    name No null The name of the campaign
    code No null The short lookup code for the campaign, presented to the customer (recommended format XXMMYY where XX is 2-letter shortenting of the name, MM and YY are the month & year)
    campaigns_category_id No null The Campaigns Category ID to list this under.
    campaigns_subcategory_id No null The Campaigns Subcategory ID to list this under (must be listed in the Campaigns Category and this Franchise)
    start_date No null The effective start date of the campaign
    end_date Yes null The effective end date of the campaign
    description Yes null An optional description
    cost Yes null How much the marketing activty costs, for ROI calculations in Reports

    Response

    Parameter Description
    campaign The new campaign data

    Delete a Campaign

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns/delete/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns/delete/4",
        method: "DELETE"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaign": {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2017-05-01T00:00:00+00:00",
            "end_date": "",
            "name": "Test Campaign",
            "code": "TESTCODE",
            "description": "Test campaign",
            "cost": 0,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": "2016-03-09T19:29:51+00:00",
            "hidden": false,
            "active": false
        }
    }
    

    This endpoint deletes a specific campaign by ID. Note that it's a "soft delete" - marking the active flag false.

    Deactivating the record will only prevent it from being searched for and added on to new jobs. To completely remove it from listings, use the hide endpoint.

    HTTP Request

    DELETE http://crm.oxifresh.com/campaigns/delete/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign to delete

    Response

    Parameter Description
    campaign The "deleted" campaign data

    Hide a Campaign

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns/hide/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns/hide/4",
        method: "DELETE"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaign": {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2017-05-01T00:00:00+00:00",
            "end_date": "",
            "name": "Test Campaign",
            "code": "TESTCODE",
            "description": "Test campaign",
            "cost": 0,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": "2016-03-09T19:29:51+00:00",
            "hidden": true,
            "active": false
        }
    }
    

    As campaigns are meant to be occasionally activated or deactivated, it's active flag is used just to prevent it from being added to new jobs.

    To fully remove a Campaign from GET listings and reports, you must instead "hide" the record. This marks the hidden flag false.

    You can un-hide a Campaign using the edit endpoint.

    HTTP Request

    GET http://crm.oxifresh.com/campaigns/hide/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign to hide

    Response

    Parameter Description
    campaign The "hidden" campaign data

    Activate a Campaign

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns/activate/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "POST");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns/activate/4",
        method: "POST"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "campaign": {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2017-05-01T00:00:00+00:00",
            "end_date": "",
            "name": "Test Campaign",
            "code": "TESTCODE",
            "description": "Test campaign",
            "cost": 0,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": "2016-03-09T19:29:51+00:00",
            "hidden": false,
            "active": true
        }
    }
    

    Opposite to delete, marks the active flag true.

    HTTP Request

    GET http://crm.oxifresh.com/campaigns/activate/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign to activate (un-delete)

    Response

    Parameter Description
    campaign The "activated (un-deleted)" campaign data

    Get a breadcrumb trail

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaigns/breadcrumbs/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaigns/breadcrumbs/4",
        method: "POST"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "breadcrumbs": [{
            "id": 10,
            "icon": "envelope-o",
            "name": "Shared Mail",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 3,
            "active": true,
            "type": "subcategories"
        }, {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "name": "ValPak",
            "active": true,
            "hidden": 0,
            "type": "campaigns"
        }, {
            "id": 1,
            "franchise_id": 2,
            "campaigns_category_id": 10,
            "campaigns_subcategory_id": 1,
            "start_date": "2018-01-01T00:00:00+00:00",
            "end_date": "2018-03-01T00:00:00+00:00",
            "name": "January Valpak 2018",
            "code": "VP0110",
            "description": "Mailed to 320,000 households on the second week of the month",
            "cost": 5785,
            "last_modified": "2016-03-09T19:29:51+00:00",
            "date_created": null,
            "hidden": false,
            "active": true,
            "selected": true
        }],
        "total": 3
    }
    

    Provides the hierarchy relationship of Campaign Category to Subcategory to Campaign.

    HTTP Request

    GET http://crm.oxifresh.com/campaigns/breadcrumnbs/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign get breadcrumb hierarchy

    Response

    Parameter Description
    breadcrumbs The ordered listing of the hierarchy of Campaign Category, Subcategory, and Campaign
    total The total number of items in the list

    Campaigns Categories

    All Marketing Campaigns are listed under a top-level Campaign Category. These Categories are set and shared app-wide, so that reports can easily be pulled across franchises on the efficacy of certain types of marketing.

    They can specify a Cost Source, indicating if the nested CampaignsCategories costs are manually entered or can be automatically determined by a different source (such as Royalty Fees).

    List Campaigns Categories

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 2
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaignsCategories": [{
            "count": 0,
            "cost": null,
            "id": 1,
            "icon": "calendar-o",
            "name": "Events",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 9,
            "active": true,
            "campaigns_categories_cost_type": {
                "id": 1,
                "label": "Manual",
                "description": "Source cost will be entered on each individual Campaign by the user.",
                "handler": "Manual",
                "options": "",
                "active": true
            }
        }, {
            "count": 50,
            "cost": 21020.46,
            "id": 2,
            "icon": "laptop",
            "name": "Internet",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 2,
            "active": true,
            "campaigns_categories_cost_type": {
                "id": 1,
                "label": "Manual",
                "description": "Source cost will be entered on each individual Campaign by the user.",
                "handler": "Manual",
                "options": "",
                "active": true
            }
        }, {
            "count": 17,
            "cost": 900,
            "id": 3,
            "icon": "print",
            "name": "Print",
            "campaigns_categories_cost_type_id": 2,
            "display_order": 4,
            "active": true,
            "campaigns_categories_cost_type": {
                "id": 2,
                "label": "NAF",
                "description": "Source cost will be calculated from NAF fees",
                "handler": "RoyaltyFee",
                "options": "",
                "active": true
            }
        }],
        "total": 3
    }
    

    This endpoint gets a paginated list of CampaignsCategories.

    HTTP Request

    GET http://crm.oxifresh.com/campaignsCategories

    Query Parameters

    No options available at this time

    Response

    Only the standard pagination parameters are available for this endpoint.

    Get a Campaign Category

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories/3');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories/3",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaignsCategory": {
            "id": 3,
            "icon": "calendar-o",
            "name": "Events",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 9,
            "active": true,
            "campaigns_categories_cost_type": {
                "id": 1,
                "label": "Manual",
                "description": "Source cost will be entered on each individual Campaign by the user.",
                "handler": "Manual",
                "options": "",
                "active": true
            }
        }
    }               
    

    This endpoint retrieves a specific campaign category by ID.

    HTTP Request

    GET http://crm.oxifresh.com/campaignsCategories/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign to retrieve

    Response

    Parameter Description
    campaignCategory The campaign category data

    Add A Campaign Category

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'name' => 'New Category',
        'icon' => 'user',
        'campaigns_categories_cost_type_id' => 1,
        'display_order' => 15
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    var data = {
        'name' :  'New Category',
        'icon' :  'user',
        'campaigns_categories_cost_type_id' :  1,
        'display_order' :  15
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories",
        method: "POST",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaignsCategory": {
            "id": 4,
            "icon": "user",
            "name": "New Category",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 15,
            "active": true
        }
    }      
    

    HTTP Request

    POST http://crm.oxifresh.com/campaignsCategories

    Data Parameters

    Parameter Optional Default Description
    name No null The name of the campaign
    icon No null The CSS class to use for the category icon
    campaigns_campaigns_categories_cost_type_id_id No null The CampaignsCategoriesCostType ID this will reference for source costs
    campaigns_display_order_id Yes null An ascending default display order for this category

    Response

    Parameter Description
    campaignsCategory The new campaign category data

    Edit A Campaign Category

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'name' => 'Different Name'
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH');
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    var data = {
        'name': 'Different Name'
    
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories/4",
        method: "PATCH",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaignsCategory": {
            "id": 4,
            "icon": "user",
            "name": "Different Name",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 15,
            "active": true
        }
    }      
    

    Edits data in an existing Campaign Category. You only need to supply fields you wish to change, but you can optionally supply all of the fields.

    HTTP Request

    PATCH http://crm.oxifresh.com/campaignsCategories/<ID>

    Data Parameters

    Parameter Optional Default Description
    name No Yes The name of the campaign
    icon No Yes The CSS class to use for the category icon
    campaigns_campaigns_categories_cost_type_id_id Yes null The CampaignsCategoriesCostType ID this will reference for source costs
    campaigns_display_order_id Yes null An ascending default display order for this category

    Response

    Parameter Description
    campaignsCategory The edited campaign category data

    Delete a Campaign Category

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories/delete/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories/4",
        method: "DELETE"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "campaignsCategory": {
            "id": 4,
            "icon": "user",
            "name": "Different Name",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 15,
            "active": false
        }
    }     
    

    This endpoint deletes a specific campaign cateogry by ID. Note that it's a "soft delete" - marking the active flag false.

    HTTP Request

    DELETE http://crm.oxifresh.com/campaignsCategories/delete/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign category to delete

    Response

    Parameter Description
    campaign The "deleted" campaign category data

    Campaign Category Breadcrumb

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/campaignsCategories/breadcrumbs/1');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories/breadcrumbs/1",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "breadcrumbs": [{
            "id": 1,
            "icon": "calendar-o",
            "name": "Events",
            "campaigns_categories_cost_type_id": 1,
            "display_order": 9,
            "active": true,
            "selected": true
        }],
        "total": 1
    }
    

    Provides the breadcrumb compatible hierarchy relationship of Campaign Category. Note that all of the Campaign-related controllers implement this, generally as an agnostic view of where each piece is in the hierarchy.

    HTTP Request

    GET http://crm.oxifresh.com/campaignsCategories/breadcrumnbs/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the campaign category to get a breadcrumb hierarchy

    Response

    Parameter Description
    breadcrumbs The listing of the hierarchy of Campaign Category
    total The total number of items in the list

    Categories

    All services and products specify a Category that helps group similar items in reports. Categories are specified at an app level and are shared across all franchises.

    Get a list of categories

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 2
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/categories?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if ($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/categories",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "categories": [
            {
                "id": 1,
                "type": "SERVICE",
                "code": "CPT",
                "name": "Carpet",
                "description": null,
                "icon_id": 2,
                "scheduling_exempt": 0,
                "display_order": 1,
                "include_in_exports": 1,
                "active": true
            },
            {
                "id": 2,
                "type": "SERVICE",
                "code": "UPHOL",
                "name": "Upholstery",
                "description": null,
                "icon_id": 1,
                "scheduling_exempt": 0,
                "display_order": 2,
                "include_in_exports": 1,
                "active": true
            },
            {
                "id": 3,
                "type": "SERVICE",
                "code": "T\u0026G",
                "name": "Tile \u0026 Grout",
                "description": "test ",
                "icon_id": 3,
                "scheduling_exempt": 0,
                "display_order": 6,
                "include_in_exports": 1,
                "active": true
            }
        ],
        "total": 3,
        "page": 1,
        "limit": 20
    }
    

    This endpoint gets a paginated list of Categories.

    HTTP Request

    GET http://crm.oxifresh.com/categories

    Query Parameters

    Only the standard pagination parameters are available for this endpoint.

    Response

    Parameter Description
    categories A paginated list of categories

    Get a Specific Category

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/categories/1');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/categories/1",
        method: "GET"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "category": {
            "id": 1,
            "type": "SERVICE",
            "code": "CPT",
            "name": "Carpet",
            "description": null,
            "icon_id": 2,
            "scheduling_exempt": 0,
            "display_order": 1,
            "include_in_exports": 1,
            "active": true,
            "services": [
                {
                    "id": 1,
                    "category_id": 1,
                    "code": "RM",
                    "name": "Room",
                    "description": "",
                    "display_order": 1,
                    "auto_add_to_quote": false,
                    "hidden": false,
                    "active": true,
                    "last_modified": "2017-11-13T14:51:02+00:00"
                },
                {
                    "id": 2,
                    "category_id": 1,
                    "code": "STR",
                    "name": "Staircase - Up to 16 steps",
                    "description": "",
                    "display_order": 3,
                    "auto_add_to_quote": false,
                    "hidden": false,
                    "active": true,
                    "last_modified": "2017-11-13T14:51:23+00:00"
                }
            ]
        }
    }
    

    This endpoint retrieves a specific category by ID, and includes a list of all the Services listed under it.

    Note that while Services are specified at an app level, each franchise sets their own pricing, duration, and descriptions in a linked 'ServicesSetting' record. The 'Service Setting' ID is what is listed on each Invoice Line Item.

    HTTP Request

    GET http://crm.oxifresh.com/categories/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the category to retrieve

    Response

    Parameter Description
    category The category data, including it's Services

    Add a new Category

    <?php
    $data = [
         "type" => "SERVICE",  
          "code" =>  "SPR",
          "name" =>  "Sprockets",
          "display_order" => 20
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/categories');
    
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString);
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data =  {
       "id": 1,
       "type": "SERVICE",
       "code": "SPR",
       "name": "Sprockets",
       "display_order": 20
    }
    baseRequest({
        url: "https://crm.oxifresh.com/api/categories",
        method: "POST",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode === 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "category": {
            "id": 20,
            "type": "SERVICE",
            "code": "SPR",
            "name": "Sprockets",
            "description": null,
            "icon_id": null,
            "scheduling_exempt": 0,
            "display_order": 20,
            "include_in_exports": 0,
            "active": true
        }
    }
    

    This endpoint creates a new Category.

    HTTP Request

    POST http://crm.oxifresh.com/categories

    Query Parameters

    Parameter Required Description
    type Yes Specifies what kind of Category this represents (either 'SERVICE' or 'PRODUCT')
    name Yes The full display name of the category
    code No A short display code, usually 3-4 characters but up to 10
    description No An optional helpful description
    display_order Yes An numerical value to help order the Category listings in the app
    scheduling_exempt No A boolean value which will indicate the Services listed under this do not impact Schedules, and therefore will not be shown in the Scheduling settings. Defaults to false
    include_in_exports No A boolean value which indicates if the automated exports system should include these services in the export. Defaults to false.
    icon_id No A legacy field which used to correspond to an icon image, not in use

    Response

    Parameter Description
    category The new category.

    Edit a Category

    <?php
    $data = [
         "code" =>  "SPRTS"
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/categories/20');
    
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH');
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data =  {
       "code": "SPRTS",
    }
    baseRequest({
        url: "https://crm.oxifresh.com/api/categories/20",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode === 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "category": {
            "id": 20,
            "type": "SERVICE",
            "code": "SPRTS",
            "name": "Sprockets",
            "description": null,
            "icon_id": null,
            "scheduling_exempt": 0,
            "display_order": 20,
            "include_in_exports": 0,
            "active": true
        }
    }
    

    This endpoint edits an existing Category.

    HTTP Request

    PATCH http://crm.oxifresh.com/categories/<ID>

    Query Parameters

    Will accept all the same parameters as the add POST endpoint. Note that you only need to include the fields you wish to change.

    Response

    Parameter Description
    category The deleted category.

    Delete a Category

    <?php
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/categories/20');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data =  {
       "code": "SPRTS"
    }
    baseRequest({
        url: "https://crm.oxifresh.com/api/categories/api/delete/20",
        method: "DELETE",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode === 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "category": {
            "id": 20,
            "type": "SERVICE",
            "code": "SPRTS",
            "name": "Sprockets",
            "description": null,
            "icon_id": null,
            "scheduling_exempt": 0,
            "display_order": 20,
            "include_in_exports": 0,
            "active": false
        }
    }
    

    Soft-deletes a Category by setting it's active flag to false

    HTTP Request

    DELETE http://crm.oxifresh.com/categories/<ID>

    Response

    Parameter Description
    category The edited category.

    Coupon Validation

    Applicability & Discounts

    <?php
    $curl                 = curl_init();
    
    // Pass a JSON-encoded string of line items data
    $lineItemsJson = [
        [
            'invoice_id' => null,
            'package_id' => null,
            'service_id' => 14421, // Room
            'product_id' => null,
            'coupon_id' => null,
            'quantity' => 1,
            'notes' => 'hey',
            'price' => 30.00,
            'discount' => 0.0,
            'taxable' => false,
            'type' => 1,
            'unit_quantity' => null,
            'priced_on' => 'per_item',
            'tax' => 0.0,
            'active' => true
        ],
        [
            'invoice_id' => null,
            'package_id' => null,
            'service_id' => null,
            'product_id' => null,
            'coupon_id' => 114, // Coupon for 2 Rooms for $58
            'quantity' => 1,
            'notes' => '',
            'price' => 0.00,
            'discount' => 0.0,
            'taxable' => false,
            'type' => 1,
            'unit_quantity' => null,
            'priced_on' => 'per_item',
            'tax' => 0.0,
            'active' => true
        ],
    ];
    
    $data = [
        'line_items' => json_encode($lineItemsJson)
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/couponsValidator/validate');      
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = [
      {
        "invoice_id": null,
        "package_id": null,
        "service_id": 14421,
        "product_id": null,
        "coupon_id": null,
        "quantity": 1,
        "notes": "hey",
        "price": 30,
        "discount": 0,
        "taxable": false,
        "type": 1,
        "unit_quantity": null,
        "priced_on": "per_item",
        "tax": 0,
        "active": true
      }, {
        "invoice_id": null,
        "package_id": null,
        "service_id": null,
        "product_id": null,
        "coupon_id": 114,
        "quantity": 1,
        "notes": "",
        "price": 0,
        "discount": 0,
        "taxable": false,
        "type": 1,
        "unit_quantity": null,
        "priced_on": "per_item",
        "tax": 0,
        "active": true
      }
    ];
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/campaignsCategories",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    The above command returns JSON with two top level nodes, overall validity of coupons listed and validation results for each individual line item (in the same order as provided). It will return JSON structured like this:

    {
        "valid": false,
        "results": [
           {
                "invoice_line_item": {
                    "invoice_id": null,
                    "quantity": 1,
                    "notes": "1",
                    "taxable": false,
                    "tax": 0,
                    "discount": 0,
                    "price": 30,
                    "priced_on": "per_item",
                    "service_id": 14421,
                    "product_id": null,
                    "coupon_id": null,
                    "active": true,
                    "services_setting": {
                        "id": 14421,
                        "franchise_id": 501,
                        "category_id": 1,
                        "code": "",
                        "name": "Room",
                        "description": "1 Room = 250 sq. ft.",
                        "price": 30,
                        "duration": 1800,
                        "unit": "",
                        "price_per_unit": null,
                        "priced_on": "per_item",
                        "taxable": true,
                        "service_id": 1,
                        "last_modified": "2016-11-09T16:15:01+00:00",
                        "active": true,
                        "category": {
                            "id": 1,
                            "type": "SERVICE",
                            "code": "CPT",
                            "name": "Carpet",
                            "description": null,
                            "icon_id": 2,
                            "scheduling_exempt": 0,
                            "display_order": 1,
                            "include_in_exports": 0,
                            "active": true
                        }
                    },
                    "category": {
                        "id": 1,
                        "type": "SERVICE",
                        "code": "CPT",
                        "name": "Carpet",
                        "description": null,
                        "icon_id": 2,
                        "scheduling_exempt": 0,
                        "display_order": 1,
                        "include_in_exports": 0,
                        "active": true
                    },
                    "total": 30,
                    "category_name": "Carpet",
                    "category_code": "CPT"
                }
            },
            {
                "invoice_line_item": {
                    "invoice_id": null,
                    "quantity": 1,
                    "notes": "1",
                    "taxable": false,
                    "tax": 0,
                    "discount": 0,
                    "price": 0,
                    "priced_on": "per_item",
                    "service_id": null,
                    "product_id": null,
                    "coupon_id": 114,
                    "active": true,
                    "total": 0,
                    "category_name": null,
                    "category_code": null
                },
                "coupon": {
                    "id": 114,
                    "franchise_id": 501,
                    "coupons_pack_id": 1,
                    "campaign_id": null,
                    "name": "2 RM 4 $50",
                    "description": "",
                    "start_date": "2017-10-11T00:00:00+00:00",
                    "expiration_date": null,
                    "created_by_employee_id": 1,
                    "last_modified_by_employee_id": 1,
                    "date_created": "2017-10-11T12:22:35+00:00",
                    "last_modified": "2017-10-11T12:22:35+00:00",
                    "cannot_be_combined": true,
                    "list_order": 0,
                    "active": true,
                    "coupons_rules": [
                        {
                            "id": 1,
                            "coupon_id": 114,
                            "description": null,
                            "is_set_wide_requirement": false,
                            "is_tiered": true,
                            "tier_order": 1,
                            "coupons_rules_type_id": 1,
                            "value": 50,
                            "discount_type": "flat",
                            "compound_type": "all",
                            "active": true,
                            "coupons_rules_items": [
                                {
                                    "id": 1,
                                    "coupons_rule_id": 1,
                                    "entity_type": "Categories",
                                    "entity_id": 1,
                                    "value": 50,
                                    "type": "min_amount",
                                    "has_substitutes": false,
                                    "substitution_type": "or",
                                    "primary_discount_item": true,
                                    "active": true,
                                    "coupons_rules_items_substitutes": [],
                                    "item": {
                                        "id": 1,
                                        "type": "SERVICE",
                                        "code": "CPT",
                                        "name": "Carpet",
                                        "description": null,
                                        "icon_id": 2,
                                        "scheduling_exempt": 0,
                                        "display_order": 1,
                                        "include_in_exports": 0,
                                        "active": true
                                    },
                                    "item_name": "Carpet"
                                }
                            ]
                        },
                        {
                            "id": 2,
                            "coupon_id": 114,
                            "description": null,
                            "is_set_wide_requirement": false,
                            "is_tiered": true,
                            "tier_order": 2,
                            "coupons_rules_type_id": 1,
                            "value": 10,
                            "discount_type": "dollar",
                            "compound_type": "all",
                            "active": true,
                            "coupons_rules_items": [
                                {
                                    "id": 2,
                                    "coupons_rule_id": 2,
                                    "entity_type": "ServicesSettings",
                                    "entity_id": 14395,
                                    "value": 2,
                                    "type": "min_quantity",
                                    "has_substitutes": false,
                                    "substitution_type": "or",
                                    "primary_discount_item": true,
                                    "active": true,
                                    "coupons_rules_items_substitutes": [
                                        {
                                            "id": 1,
                                            "coupons_rules_item_id": 2,
                                            "entity_type": "Categories",
                                            "entity_id": 1,
                                            "value": 60,
                                            "type": "min_amount",
                                            "active": true,
                                            "item": {
                                                "id": 1,
                                                "type": "SERVICE",
                                                "code": "CPT",
                                                "name": "Carpet",
                                                "description": null,
                                                "icon_id": 2,
                                                "scheduling_exempt": 0,
                                                "display_order": 1,
                                                "include_in_exports": 0,
                                                "active": true
                                            },
                                            "item_name": "Carpet"
                                        }
                                    ],
                                    "item": {
                                        "id": 14395,
                                        "franchise_id": 501,
                                        "category_id": 1,
                                        "code": "",
                                        "name": "Room",
                                        "description": "1 Room = 250 sq. ft.",
                                        "price": 30,
                                        "duration": 1800,
                                        "unit": "",
                                        "price_per_unit": null,
                                        "priced_on": "per_item",
                                        "taxable": true,
                                        "service_id": 1,
                                        "last_modified": "2016-11-09T16:15:01+00:00",
                                        "active": true
                                    },
                                    "item_name": "Room"
                                }
                            ]
                        }
                    ],
                    "expired": false
                },
                "valid": false,
                "missing": [
                    {
                        "rule": {
                            "id": 2,
                            "coupon_id": 114,
                            "description": null,
                            "is_set_wide_requirement": false,
                            "is_tiered": true,
                            "tier_order": 2,
                            "coupons_rules_type_id": 1,
                            "value": 10,
                            "discount_type": "dollar",
                            "compound_type": "all",
                            "active": true
                        },
                        "missing_items": [
                            {
                                "missing_amount": 1,
                                "item_price": 60,
                                "item": {
                                    "id": 2,
                                    "coupons_rule_id": 2,
                                    "entity_type": "ServicesSettings",
                                    "entity_id": 14395,
                                    "value": 2,
                                    "type": "min_quantity",
                                    "has_substitutes": false,
                                    "substitution_type": "or",
                                    "primary_discount_item": true,
                                    "active": true,
                                    "coupons_rules_items_substitutes": [
                                        {
                                            "id": 1,
                                            "coupons_rules_item_id": 2,
                                            "entity_type": "Categories",
                                            "entity_id": 1,
                                            "value": 60,
                                            "type": "min_amount",
                                            "active": true,
                                            "item_name": "Carpet"
                                        }
                                    ],
                                    "item": {
                                        "id": 14395,
                                        "franchise_id": 501,
                                        "category_id": 1,
                                        "code": "",
                                        "name": "Room",
                                        "description": "1 Room = 250 sq. ft.",
                                        "price": 30,
                                        "duration": 1800,
                                        "unit": "",
                                        "price_per_unit": null,
                                        "priced_on": "per_item",
                                        "taxable": true,
                                        "service_id": 1,
                                        "last_modified": "2016-11-09T16:15:01+00:00",
                                        "active": true
                                    },
                                    "item_name": "Room"
                                },
                                "substitutes": [
                                    {
                                        "missing_amount": 30,
                                        "item_price": 60,
                                        "item": {
                                            "id": 1,
                                            "coupons_rules_item_id": 2,
                                            "entity_type": "Categories",
                                            "entity_id": 1,
                                            "value": 60,
                                            "type": "min_amount",
                                            "active": true,
                                            "item": {
                                                "id": 1,
                                                "type": "SERVICE",
                                                "code": "CPT",
                                                "name": "Carpet",
                                                "description": null,
                                                "icon_id": 2,
                                                "scheduling_exempt": 0,
                                                "display_order": 1,
                                                "include_in_exports": 0,
                                                "active": true
                                            },
                                            "item_name": "Carpet"
                                        }
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "rule": {
                            "id": 1,
                            "coupon_id": 114,
                            "description": null,
                            "is_set_wide_requirement": false,
                            "is_tiered": true,
                            "tier_order": 1,
                            "coupons_rules_type_id": 1,
                            "value": 50,
                            "discount_type": "flat",
                            "compound_type": "all",
                            "active": true
                        },
                        "missing_items": [
                            {
                                "missing_amount": 20,
                                "item_price": 50,
                                "item": {
                                    "id": 1,
                                    "coupons_rule_id": 1,
                                    "entity_type": "Categories",
                                    "entity_id": 1,
                                    "value": 50,
                                    "type": "min_amount",
                                    "has_substitutes": false,
                                    "substitution_type": "or",
                                    "primary_discount_item": true,
                                    "active": true,
                                    "coupons_rules_items_substitutes": [],
                                    "item_name": "Carpet"
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    }
    

    When valid, each discounted item will list a discounts node, specifying the amount of items discounted (specified either as a quantity or price), and the 'discount' amount in dollars:

    {
        "invoice_line_item": {
            "invoice_id": null,
            "quantity": 2,
            "notes": "1",
            "taxable": false,
            "tax": 0,
            "discount": 0,
            "price": 30,
            "priced_on": "per_item",
            "service_id": 14395,
            "product_id": null,
            "coupon_id": null,
            "active": true,
            "total": 60,
            "category_name": "Carpet",
            "category_code": "CPT"
        },
        "discounts": [
            {
                "id": 0,
                "amount_discounted": 2,
                "discount_amount_type": "quantity",
                "discount": 10
            }
        ]
    }
    

    This endpoint validates that coupons apply (or don't apply) in a list of line items, and calculates discount amounts for items discounted by coupons.

    HTTP Request

    GET http://crm.oxifresh.com/couponsValidator/validate

    Query Parameters

    Parameter Description
    line_items A JSON-encoded string of line items data, including any Coupons to validate as line items themselves.

    Response

    Parameter Description
    valid True if all coupons are valid or if no Coupons are listed
    results A list of the line items provided but decorated with discounts, or with Coupon applicability information. See below for detailed explanations

    Service/Product Results

    Each-non Coupon line item will list a 'discounts' node, if the line item was automatically discounted.

    See an example on the right.

    Parameter Description
    discounts An array of discount information. Lists how many items were applicable in amount_discounted, the type used in discount_amount_type (quantity or price) and the total $ amount to take off in discount.

    Coupon Results

    Note that each coupon listed in line_items will list the following in results:

    Parameter Description
    coupon The extended rules, items, and substitutes listed for that Coupon ID.
    valid If the coupon applied, it was 'valid'. If it's not valid, you may need to remove it or prompt the user to add additional items to make it valid.
    missing If not valid, it will list a node for each Rule that wasn't valid, containing a list of what items are required to add to make it valid.

    Generally, it's expected you'll loop through the missing nodes to build suggestions on what services the customer can add to make the coupon apply.

    In the example above, results[1][missing][0][missing_items][0][missing_amount] shows it's missing 1 of the item, whose name is found in results[1][missing][0][missing_items][0][item][item_name], "Room".

    It lists a substitute for the missing Room, results[1][missing][0][missing_items][0][substitutes][0][item][item_name] any Category "Carpet", totaling results[1][missing][0][missing_items][0][substitutes][0][missing_amount], $30 (the type is 'min_amount' so it's referring to dollars rather than Quantities)

    Employees

    Employees are the application's user accounts.

    Get a list of employees

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/employees?franchise_id=501');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    

    This will return JSON results structured like this:

    {
        "employees": [
            {
                "id": 4,
                "franchise_id": 501,
                "group_id": 7,
                "title": "",
                "first_name": "TestFranchise",
                "middle_initial": "",
                "last_name": "Owner",
                "suffix": "",
                "address_1": "575 Lincoln St",
                "address_2": "",
                "city": "Denver",
                "state": "CO",
                "zipcode": 80203,
                "telephone": "7205322733",
                "mobile": "3035551234",
                "email": "example+testing@oxifresh.com",
                "start_date": null,
                "birth_date": null,
                "username": "testingfranchiseowner",
                "color": "000000",
                "ext_user_id": "",
                "schedule": true,
                "bridge_controlled": false,
                "verified_email": false,
                "display_order": 1,
                "active": true,
                "group": {
                    "id": 7,
                    "name": "Franchise Owner",
                    "corporate": false,
                    "admin_rank": 70,
                    "display_order": 2,
                    "active": true
                }
            },
            {
                "id": 5,
                "franchise_id": 501,
                "group_id": 5,
                "title": null,
                "first_name": "TestFranchise",
                "middle_initial": "",
                "last_name": "Tech",
                "suffix": null,
                "address_1": "575 Lincoln St",
                "address_2": "",
                "city": "Denver",
                "state": "CO",
                "zipcode": 80203,
                "telephone": "7205322732",
                "mobile": "3035551234",
                "email": "example+devtest_TestFranchise@oxifresh.com",
                "start_date": null,
                "birth_date": null,
                "username": "testingfranchisetech",
                "color": "1eba2d",
                "ext_user_id": null,
                "schedule": true,
                "bridge_controlled": false,
                "verified_email": false,
                "display_order": 3,
                "active": true,
                "group": {
                    "id": 5,
                    "name": "Techs",
                    "corporate": false,
                    "admin_rank": 40,
                    "display_order": 1,
                    "active": true
                }
            }
        ],
        "total": 3
    }
    
    

    This endpoint gets a paginated list of Employees.

    HTTP Request

    GET http://crm.oxifresh.com/employees

    Query Parameters

    Parameter Default Description
    search null A generic search term that will return employees with matching employee names, usernames, emails or phone numbers

    Response

    Parameter Description
    employees A paginated list of employees, includes the linked Group, and for all-franchise lists the linked Franchise
    total The total number of employees matching the input criteria

    Get a Specific Employee

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/employees/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    

    This will return JSON results structured like this:

    {
        "employee": {
            "id": 4,
            "franchise_id": 501,
            "group_id": 7,
            "title": "",
            "first_name": "TestFranchise",
            "middle_initial": "",
            "last_name": "Owner",
            "suffix": "",
            "address_1": "575 Lincoln St",
            "address_2": "",
            "city": "Denver",
            "state": "CO",
            "zipcode": 80203,
            "telephone": "7205322733",
            "mobile": "3035551234",
            "email": "example+testing@oxifresh.com",
            "start_date": null,
            "birth_date": null,
            "username": "testingfranchiseowner",
            "color": "000000",
            "ext_user_id": "",
            "schedule": true,
            "bridge_controlled": false,
            "verified_email": false,
            "display_order": 1,
            "active": true,
            "group": {
                "id": 7,
                "name": "Franchise Owner",
                "corporate": false,
                "admin_rank": 70,
                "display_order": 2,
                "active": true
            }
        }
    }
    

    This endpoint retrieves a employee by ID.

    HTTP Request

    GET http://crm.oxifresh.com/employees/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the employee to retrieve

    Response

    Parameter Description
    employee The employee data

    Add A Employee

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/employees');    
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'franchise_id' => 501,
        'first_name' => 'John',
        'last_name' => 'Smith',
        'group_id' => 5,
        'username' => 'john.smith'
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    

    This will return JSON results structured like this:

    {
        "employee": {
            "id": 6,
            "franchise_id": 501,
            "group_id": 5,
            "title": "",
            "first_name": "John",
            "middle_initial": "",
            "last_name": "Smith",
            "suffix": "",
            "address_1": "",
            "address_2": "",
            "city": "",
            "state": "",
            "zipcode": "",
            "telephone": "",
            "mobile": "",
            "email": "",
            "start_date": null,
            "birth_date": null,
            "username": "john.smith",
            "color": "000000",
            "ext_user_id": "",
            "schedule": false,
            "bridge_controlled": false,
            "verified_email": false,
            "display_order": 1,
            "active": true,
            "group": {
                "id": 5,
                "name": "Techs",
                "corporate": false,
                "admin_rank": 50,
                "display_order": 3,
                "active": true
            }
        }
    }
    

    This endpoint adds a new Employee to the system.

    HTTP Request

    POST http://crm.oxifresh.com/employees

    Data Parameters

    Note that you may pass values for any of the fields listed in GET/<id>, but the following are required.

    Parameter Optional Default Description
    first_name* No null First Name
    last_name* Yes null Last Name
    username* Yes null A unique login username
    group_id* Yes null The ID for the Group to assign this user to. Note that you won't be able to create users in Groups above your own Admin Rank

    *Required

    Response

    Parameter Description
    employee The new employee data.

    Delete an Employee

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/employees/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    

    This will return JSON results structured like this:

    {
        "employee": {
            "id": 4,
            "franchise_id": 501,
            "group_id": 7,
            "title": "",
            "first_name": "TestFranchise",
            "middle_initial": "",
            "last_name": "Owner",
            "suffix": "",
            "address_1": "575 Lincoln St",
            "address_2": "",
            "city": "Denver",
            "state": "CO",
            "zipcode": 80203,
            "telephone": "7205322733",
            "mobile": "3035551234",
            "email": "example+testing@oxifresh.com",
            "start_date": null,
            "birth_date": null,
            "username": "testingfranchiseowner",
            "color": "000000",
            "ext_user_id": "",
            "schedule": true,
            "bridge_controlled": false,
            "verified_email": false,
            "display_order": 1,
            "active": false
        }
    }
    

    This endpoint deletes a specific Employee by ID. Note that it's a "soft delete" - marking the active flag false. This removes it from GET lists and aggregate employees, and prevents the user from logging in.

    Note that they will be visible in the Calendar until you set schedule to false as well.

    HTTP Request

    DELETE http://crm.oxifresh.com/employees/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the employee to delete

    Response

    Parameter Description
    employee The "deleted" employee data

    Invoices

    All jobs are linked to a single entity technically called an 'invoice'.

    These invoices have a invoices_status_id which indicates if the record represents a:

    The IDs for these statuses can be pulled from the invoicesStatuses endpoint.

    The status ID is modified as the record progressed from one state to another, usually ending as either Paid or Voided.

    These 'invoice' records are locked once they reach a Paid state, and can't be modified unless reverted back to a Work Order.

    Get a list of invoices

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 2
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/invoices?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {franchsie_id: 2}
    baseRequest({
        url: "https://crm.oxifresh.com/api/invoices",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "invoices": [
            {
                "id": 885632,
                "franchise_id": 2,
                "invoice_status_id": 2,
                "contact_id": 912099,
                "employee_id": 4151,
                "work_address_id": 965678,
                "campaign_id": 10896,
                "location_id": 2,
                "sequence": 28374,
                "po_number": null,
                "created_by_id": 2764,
                "date_created": "2017-10-03T12:00:03+00:00",
                "last_modified": "2017-10-03T06:00:03+00:00",
                "date_last_exported": null,
                "date_due": "2017-10-03T12:00:03+00:00",
                "date_quoted": "2017-10-03T12:00:03+00:00",
                "date_completed": "2017-11-01T12:00:00+00:00",
                "date_paid": null,
                "notes": "Booked Online",
                "subtotal": 270,
                "tax_percent": 0.074999999999999997,
                "tax_due": 0,
                "amount_due": 270,
                "amount_paid": 0,
                "original_subtotal": 0,
                "quote_confirmed": false,
                "imported": false,
                "active": true,
                "invoices_campaigns": [
                    {
                        "id": 18629,
                        "franchise_id": 2,
                        "invoice_id": 885632,
                        "campaign_id": 10896,
                        "no_code_explanation": null,
                        "date_created": "2017-10-05T10:34:42+00:00",
                        "last_modified": "2017-10-05T10:34:42+00:00",
                        "created_by_employee_id": 2764,
                        "last_modified_by_employee_id": 2764,
                        "active": true,
                        "campaign": {
                            "id": 10896,
                            "franchise_id": 2,
                            "coupons_pack_id": 2,
                            "campaigns_category_id": 2,
                            "campaigns_subcategory_id": 31,
                            "start_date": "2012-05-01T00:00:00+00:00",
                            "end_date": null,
                            "name": "OS2500",
                            "code": "OS2500",
                            "description": "",
                            "cost": null,
                            "last_modified": "2017-10-10T17:36:36+00:00",
                            "date_created": null,
                            "hidden": false,
                            "active": true
                        }
                    }
                ],
                "address": {
                    "id": 965678,
                    "entity_type": 4,
                    "entity_id": 912099,
                    "template_id": 1,
                    "type_id": 1,
                    "template_group_id": null,
                    "address1": "15296 W Warren Ave",
                    "address2": null,
                    "unit_type": null,
                    "unit_number": "",
                    "county": null,
                    "city": "Denver",
                    "state": "CO",
                    "zipcode": "80228",
                    "country_id": 1,
                    "active": 1,
                    "latitude": null,
                    "longitude": null,
                    "unit_type_name": null,
                    "full_street": "15296 W Warren Ave",
                    "country_code": "US",
                    "type_name": null
                },
                "contact": {
                    "id": 912099,
                    "franchise_id": 2,
                    "type_id": 1,
                    "classification_id": 1,
                    "status_id": 2,
                    "campaign_id": null,
                    "title": null,
                    "first_name": "Anita ",
                    "middle_initial": null,
                    "last_name": "Ritchie",
                    "suffix": null,
                    "date_of_birth": null,
                    "gender": null,
                    "telephone": "8322283443",
                    "telephone2": null,
                    "telephone3": null,
                    "telephone4": null,
                    "email": "anitaritchie@icloud.com",
                    "url": null,
                    "account_number": null,
                    "company_name": null,
                    "date_created": "2017-10-03T12:00:03+00:00",
                    "last_modified": "2017-10-03T06:00:03+00:00",
                    "date_called": null,
                    "hide": false,
                    "active": true,
                    "unsubscribed": false,
                    "constant_contact_id": null,
                    "full_name": "Anita  Ritchie"
                },
                "category_ids": [
                    1,
                    5
                 ],
                "is_completed": false
            }
        ],
        "total": 2358,
        "page": 1,
        "limit": 1
    }
    

    This endpoint gets a paginated list of Invoices. It can be sorted and filtered by passing extra parameters, outlined below.

    Filtering & Date Limiting

    Whenever an Invoice or it's line items are updated, it's last_modified field is updated. You can limit results to those updated after a specific date with the last_modified_after parameter.

    Optionally, it can return a list of the invoice's line item's Category IDs by passing include_categories. You can dereference these using the categories API endpoint.

    Similarly, you can request a list of the Campaigns linked to each invoice with include_campaigns, and even filter results to
    those that match a specific Campaign code with campaign_code.

    Bulk Listings

    The default page size is just 10 records, but you can increase that limit significantly with limit.

    If the limit is too high, requests can timeout or overflow and the API will return a 500-coded response.

    HTTP Request

    GET http://crm.oxifresh.com/invoices

    Query Parameters

    Parameter Default Example Description
    last_modified_after null 2018-03-01 17:52:12 Restricts list to those that have been updated after the date specified (formatted YYYY-MM-DD or YYYY-MM-DD HH:MM:SS). Time is UTC.
    date_created_after null 2018-03-01 17:52:12 Set to true to include invoices that were not indicated to be shown publicly
    date_completed_after null 2018-03-01 17:52:12 Restricts results to those who are scheduled to be completed after the specified date
    include_categories null true Include a list of the unique Category IDs this invoice has listed in it's line items
    include_summary false true Include a human-readable summary of the total & invoice line items
    include_campaigns false true Include a list of the Invoice Campaign records and their linked Campaign
    campaign_code null OS2500 Filter list to jobs that were associated with the specified Marketing Campaign code
    contact_id null 22456 Limit to only invoices that match the specified contact ID

    Response

    Parameter Description
    invoices A paginated list of invoices, includes the Contact and service Address records by default.

    Invoice Response Details

    Each of invoices will contain the following fields:

    Parameter Example Description
    franchise_id 2 The franchise ID this invoice belongs to (immutable)
    invoice_status_id 2 The type of invoice (Quote/Work Order/Invoice/Paid/Void)
    contact_id 912099 The ID of the Contact this invoice is for
    employee_id 4151 The ID of the Employee that is assigned to do this work
    work_address_id 965678 The ID of the Address the job is scheduled to take place
    campaign_id 10896 Deprecated - This will be removed soon, the same information is now available in the invoices_campaigns parameter
    location_id 2 The ID of the Location this job is located in. Can be changed until date_last_exported is set.
    sequence 28374 The auto-incremented number of the invoice, unique to the Franchise (ie. all franchises usually start at 1)
    po_number null A PO Number, not currently in use
    created_by_id 2764 The ID of the Employee that created this Invoice
    date_created "2017-10-03T12:00:03+00:00" The date it was created
    last_modified "2017-10-03T06:00:03+00:00" The date it was last modified, or it's Invoice Line Items were modified
    date_last_exported null Date it was last exported to a 3rd party vendor. If not null, Location ID becomes Immutable.
    date_due "2017-10-03T12:00:03+00:00" Date payment is due
    date_quoted "2017-10-03T12:00:03+00:00" Date the Quote (if applicable) was created
    date_completed "2017-11-01T12:00:00+00:00" Date the job was actually done (should syncronize with it's Appointment datetime)
    date_paid null Date the invoice was marked Paid
    notes "Booked Online" Any custom notes entered manually
    subtotal 270 Pre-tax subtotal
    tax_percent 0.074999999999999997 The tax rate
    tax_due 0 The effect amount of tax due (note that some line items may not be taxable, so this is not necessarily the subtotal times the tax_percent)
    amount_due 270 The post-tax total amount, due in full
    amount_paid 0 The total of any payments made on this invoice
    original_subtotal 0 A record of the pre-tax subtotal when the job was originally booked as a Work Order (the final total may be different, the effective difference is the 'Upsell amount')
    quote_confirmed false A flag which when false indicates edits to subtotal should update original_subtotal
    imported false A flag to indicate the invoice was imported from a 3rd party source
    active true If false, the record is treated as if it were hard-deleted
    is_completed true Simplified flag to indicate if the invoice work has been marked as closed out and "completed"

    It will also list any of the following foreign-keyed associations, depending on what data was requested:

    Parameter Description
    address The service Address for the job, includes the street address information & geocode
    contact The Contact record, with their name, phone, email
    category_ids If include_categories was specified, an array of the distinct Category IDs that made up the line items
    summary If include_summary was specified, a human-readable summary of the job including the total and a list of service quantites and codes
    invoices_campaigns If include_campaigns was specified, an array of all the linked Invoice Campaigns, which in turn list the marketing Campaign

    Get a Specific Invoice

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/invoices/1');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/invoices/1",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "invoice": {
            "id": 885632,
            "franchise_id": 2,
            "invoice_status_id": 2,
            "contact_id": 912099,
            "employee_id": 4151,
            "work_address_id": 965678,
            "campaign_id": 10896,
            "location_id": 2,
            "sequence": 28374,
            "po_number": null,
            "created_by_id": 2764,
            "date_created": "2017-10-03T12:00:03+00:00",
            "last_modified": "2017-10-03T06:00:03+00:00",
            "date_last_exported": null,
            "date_due": "2017-10-03T12:00:03+00:00",
            "date_quoted": "2017-10-03T12:00:03+00:00",
            "date_completed": "2017-11-01T12:00:00+00:00",
            "date_paid": null,
            "notes": "Booked Online",
            "subtotal": 270,
            "tax_percent": 0.074999999999999997,
            "tax_due": 0,
            "amount_due": 270,
            "amount_paid": 0,
            "original_subtotal": 0,
            "quote_confirmed": false,
            "imported": false,
            "active": true,
            "invoices_campaigns": [
                {
                    "id": 18629,
                    "franchise_id": 2,
                    "invoice_id": 885632,
                    "campaign_id": 10896,
                    "no_code_explanation": null,
                    "date_created": "2017-10-05T10:34:42+00:00",
                    "last_modified": "2017-10-05T10:34:42+00:00",
                    "created_by_employee_id": 2764,
                    "last_modified_by_employee_id": 2764,
                    "active": true,
                    "campaign":  {
                        "id": 10896,
                        "franchise_id": 2,
                        "coupons_pack_id": null,
                        "campaigns_category_id": 10,
                        "campaigns_subcategory_id": 1,
                        "start_date": "2018-01-01T00:00:00+00:00",
                        "end_date": null,
                        "name": "Feb Valpak 2018",
                        "code": "VP0218",
                        "description": "Sent to all the cool kids",
                        "cost": 1337,
                        "last_modified": "2018-03-19T12:29:51+00:00",
                        "date_created": "2018-03-09T12:29:51+00:00",
                        "hidden": false,
                        "active": false
                     }
                }
            ],
            "invoice_line_items": [
                {
                    "id": 3959184,
                    "invoice_id": 885632,
                    "package_id": null,
                    "service_id": null,
                    "product_id": null,
                    "coupon_id": 522,
                    "quantity": 1,
                    "notes": "",
                    "price": 0,
                    "discount": 0,
                    "taxable": false,
                    "tax": 0,
                    "overridden_by_employee_id": null,
                    "overridden_date": null,
                    "active": true,
                    "total": 0,
                    "category_name": null,
                    "category_code": null,
                    "name": null
                },
                {
                    "id": 3959185,
                    "invoice_id": 885632,
                    "package_id": null,
                    "service_id": 3911,
                    "product_id": null,
                    "coupon_id": null,
                    "quantity": 5,
                    "notes": "",
                    "price": 45,
                    "discount": -67,
                    "taxable": false,
                    "tax": 0,
                    "overridden_by_employee_id": null,
                    "overridden_date": null,
                    "active": true,
                    "total": 158,
                    "category_name": "Carpet",
                    "category_code": "CPT",
                    "name": "Room"
                },
                {
                    "id": 3959186,
                    "invoice_id": 885632,
                    "package_id": null,
                    "service_id": 3921,
                    "product_id": null,
                    "coupon_id": null,
                    "quantity": 2,
                    "notes": "",
                    "price": 50,
                    "discount": 0,
                    "taxable": false,
                    "tax": 0,
                    "overridden_by_employee_id": null,
                    "overridden_date": null,
                    "active": true,
                    "total": 100,
                    "category_name": "Carpet",
                    "category_code": "CPT",
                    "name": "Staircase - Up to 16 steps"
                },
                {
                    "id": 3959187,
                    "invoice_id": 885632,
                    "package_id": null,
                    "service_id": 4171,
                    "product_id": null,
                    "coupon_id": null,
                    "quantity": 1,
                    "notes": "",
                    "price": 12,
                    "discount": 0,
                    "taxable": false,
                    "tax": 0,
                    "overridden_by_employee_id": null,
                    "overridden_date": null,
                    "active": true,
                    "total": 12,
                    "category_name": "Service Charge",
                    "category_code": "MISC",
                    "name": "Service Charge"
                }
            ],
            "address": {
                "id": 965678,
                "entity_type": 4,
                "entity_id": 912099,
                "template_id": 1,
                "type_id": 1,
                "template_group_id": null,
                "address1": "15296 W Warren Ave",
                "address2": null,
                "unit_type": null,
                "unit_number": "",
                "county": null,
                "city": "Denver",
                "state": "CO",
                "zipcode": "80228",
                "country_id": 1,
                "active": 1,
                "latitude": null,
                "longitude": null,
                "unit_type_name": null,
                "full_street": "15296 W Warren Ave",
                "country_code": "US",
                "type_name": null
            },
            "contact": {
                "id": 912099,
                "franchise_id": 2,
                "type_id": 1,
                "classification_id": 1,
                "status_id": 2,
                "campaign_id": null,
                "title": null,
                "first_name": "Anita ",
                "middle_initial": null,
                "last_name": "Ritchie",
                "suffix": null,
                "date_of_birth": null,
                "gender": null,
                "telephone": "8322283443",
                "telephone2": null,
                "telephone3": null,
                "telephone4": null,
                "email": "anitaritchie@icloud.com",
                "url": null,
                "account_number": null,
                "company_name": null,
                "date_created": "2017-10-03T12:00:03+00:00",
                "last_modified": "2017-10-03T06:00:03+00:00",
                "date_called": null,
                "hide": false,
                "active": true,
                "unsubscribed": false,
                "constant_contact_id": null,
                "full_name": "Anita  Ritchie"
            },
            "invoice_status": {
                "id": 2,
                "label": "Work Orders",
                "label_singular": "Work Order",
                "lookup_key": "work_order",
                "date_update_key": "",
                "date_key": "date_completed",
                "display_order": 2,
                "active": true
            },
            "is_completed": false
        }
    }
    

    This endpoint retrieves a specific invoice by ID.

    HTTP Request

    GET http://crm.oxifresh.com/invoices/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the invoice to retrieve

    Response

    Parameter Description
    invoice The invoice data, including it's Contact, service Address, the list of Invoice Line Items, and Invoice Status

    Invoice Response

    The invoice parameter should contain the following properties:

    Parameter Example Description
    franchise_id 2 The franchise ID this invoice belongs to (immutable)
    invoice_status_id 2 The type of invoice (Quote/Work Order/Invoice/Paid/Void)
    contact_id 912099 The ID of the Contact this invoice is for
    employee_id 4151 The ID of the Employee that is assigned to do this work
    work_address_id 965678 The ID of the Address the job is scheduled to take place
    campaign_id 10896 Deprecated - This will be removed soon, the same information is now available in the invoices_campaigns parameter
    location_id 2 The ID of the Location this job is located in. Can be changed until date_last_exported is set.
    sequence 28374 The auto-incremented number of the invoice, unique to the Franchise (ie. all franchises usually start at 1)
    po_number null A PO Number, not currently in use
    created_by_id 2764 The ID of the Employee that created this Invoice
    date_created "2017-10-03T12:00:03+00:00" The date it was created
    last_modified "2017-10-03T06:00:03+00:00" The date it was last modified, or it's Invoice Line Items were modified
    date_last_exported null Date it was last exported to a 3rd party vendor. If not null, Location ID becomes Immutable.
    date_due "2017-10-03T12:00:03+00:00" Date payment is due
    date_quoted "2017-10-03T12:00:03+00:00" Date the Quote (if applicable) was created
    date_completed "2017-11-01T12:00:00+00:00" Date the job was actually done (should syncronize with it's Appointment datetime)
    date_paid null Date the invoice was marked Paid
    notes "Booked Online" Any custom notes entered manually
    subtotal 270 Pre-tax subtotal
    tax_percent 0.074999999999999997 The tax rate
    tax_due 0 The effect amount of tax due (note that some line items may not be taxable, so this is not necessarily the subtotal times the tax_percent)
    amount_due 270 The post-tax total amount, due in full
    amount_paid 0 The total of any payments made on this invoice
    original_subtotal 0 A record of the pre-tax subtotal when the job was originally booked as a Work Order (the final total may be different, the effective difference is the 'Upsell amount')
    quote_confirmed false A flag which when false indicates edits to subtotal should update original_subtotal
    imported false A flag to indicate the invoice was imported from a 3rd party source
    active true If false, the record is treated as if it were hard-deleted
    is_completed true Simplified flag to indicate if the invoice work has been marked as closed out and "completed"

    It will also list any of the following foreign-keyed associations:

    Parameter Description
    address The service Address for the job, includes the street address information & geocode
    contact The Contact record, with their name, phone, email
    invoices_campaigns An array of all the linked Invoice Campaigns, which in turn list the marketing Campaign
    invoice_line_items A list of all the Invoice Line Items in the invoice

    Locations

    Franchise Locations are geographical subgroups of zipcodes, which split a single franchise into multiple territories.

    Each Location has a short code, a localized name, a set of metadata, and a list of zipcodes/postal codes which comprise the areas.

    Get a list of locations

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 501
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/locations?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {franchsie_id: 501}
    baseRequest({
        url: "https://crm.oxifresh.com/api/locations",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "locations": [
            {
                "total_zipcodes": 19,
                "id": 2060,
                "code": "XX-0501A",
                "label": "Location of Edgewater Carpet Cleaning",
                "franchise_id": 501,
                "is_default": true,
                "phone": "303-555-1234",
                "last_modified": "2016-10-06T19:22:16+00:00",
                "active": true,
                "full_name": "XX-0501A - Location of Edgewater Carpet Cleaning"
            },
            {
                "total_zipcodes": 8,
                "id": 2061,
                "code": "XX-0501B",
                "label": "Location of Lakewood Carpet Cleaning",
                "franchise_id": 501,
                "is_default": false,
                "phone": "303-555-4444",
                "last_modified": "2016-10-05T19:53:25+00:00",
                "active": true,
                "full_name": "XX-0501B - Location of Lakewood Carpet Cleaning"
            }
        ],
        "total": 2
    }
    

    This endpoint a paginated list of Locations.

    HTTP Request

    GET http://crm.oxifresh.com/locations

    Query Parameters

    Parameter Default Description
    search null A generic search term that will return locations with matching names, phone numbers, or Codes
    showMetadata false Returns a list with all the detailed metadata for each location.

    For example, showMetadata will add the following locations_metadata node to each location:

    {
        "locations": [{
            "total_zipcodes": 19,
            "id": 2060,
            "code": "XX-0501A",
            "label": "Location of Edgewater Carpet Cleaning",
            "franchise_id": 501,
            "is_default": true,
            "phone": "303-555-1234",
            "last_modified": "2016-10-06T19:22:16+00:00",
            "active": true,
            "locations_metadata": {
                "id": 1,
                "location_id": 1,
                "date_created": "2018-05-14T16:15:55+00:00",
                "last_modified": "2018-07-31T08:00:12+00:00",
                "address1": "143 Union St",
                "address2": null,
                "city": "Denver",
                "state": "CO",
                "zipcode": "80228",
                "country_code": "US",
                "territory_name_short": null,
                "service_locations": "Edgewater, Denver, Wheat Ridge, Applewood, Lakewood",
                "monday_hours_of_operation": "8:00-19:00",
                "tuesday_hours_of_operation": "8:00-19:00",
                "wednesday_hours_of_operation": "8:00-19:00",
                "thursday_hours_of_operation": "8:00-19:00",
                "friday_hours_of_operation": "8:00-19:00",
                "saturday_hours_of_operation": "8:00-17:00",
                "sunday_hours_of_operation": "",
                "about_blurb": "A paragraph of text about the location...",
                "in_the_community_blurb": "A paragraph about the local community ...",
                "owner_bio_blurb": "A little bio about the owner(s) ...",
                "owner_image_post_id": 1234,
                "owner_name": "Rob White",
                "google_verification_snippet": "",
                "facebook_url": "https:\/\/www.facebook.com\/oxifresh\/reviews\/",
                "google_url": "https:\/\/www.google.com\/search?q=Oxi+Fresh+of ...",
                "mobile_google_url": "https:\/\/www.google.com\/search?q=Oxi+ ....",
                "linkedin_url": null,
                "twitter_url": null,
                "yelp_url": "https:\/\/www.yelp.com\/biz\/ ...",
                "instagram_url": null
            },
            "full_name": "XX-0501A - Location of Edgewater Carpet Cleaning"
        }],
        "total": 9,
        "page": 1,
        "limit": 1
    }
    

    Response

    Parameter Description
    locations A paginated list of locations, includes the relevant Location, Location Source, and if listed Contact and Invoice information.
    total The total number of locations matching the input criteria

    Get a Specific Location

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/locations/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/locations/4",
        method: "GET"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "location": {
            "id": 2060,
            "code": "XX-0501A",
            "label": "Location of Edgewater Carpet Cleaning",
            "franchise_id": 501,
            "is_default": true,
            "phone": "303-232-0120",
            "last_modified": "2016-10-06T19:22:16+00:00",
            "active": true,
            "locations_metadata": {
                   "id": 1,
                   "location_id": 1,
                   "date_created": "2018-05-14T16:15:55+00:00",
                   "last_modified": "2018-07-31T08:00:12+00:00",
                   "address1": "143 Union St",
                   "address2": null,
                   "city": "Denver",
                   "state": "CO",
                   "zipcode": "80228",
                   "country_code": "US",
                   "territory_name_short": null,
                   "service_locations": "Edgewater, Denver, Wheat Ridge, Applewood, Lakewood",
                   "monday_hours_of_operation": "8:00-19:00",
                   "tuesday_hours_of_operation": "8:00-19:00",
                   "wednesday_hours_of_operation": "8:00-19:00",
                   "thursday_hours_of_operation": "8:00-19:00",
                   "friday_hours_of_operation": "8:00-19:00",
                   "saturday_hours_of_operation": "8:00-17:00",
                   "sunday_hours_of_operation": "",
                   "about_blurb": "A paragraph of text about the location...",
                   "in_the_community_blurb": "A paragraph about the local community ...",
                   "owner_bio_blurb": "A little bio about the owner(s) ...",
                   "owner_image_post_id": 1234,
                   "owner_name": "Rob White",
                   "google_verification_snippet": "",
                   "facebook_url": "https:\/\/www.facebook.com\/oxifresh\/reviews\/",
                   "google_url": "https:\/\/www.google.com\/search?q=Oxi+Fresh+of ...",
                   "mobile_google_url": "https:\/\/www.google.com\/search?q=Oxi+ ....",
                   "linkedin_url": null,
                   "twitter_url": null,
                   "yelp_url": "https:\/\/www.yelp.com\/biz\/ ...",
                   "instagram_url": null
               },
            "full_name": "XX-0501A - Location of Edgewater Carpet Cleaning"
        }
    }
    

    This endpoint retrieves a specific location by ID, including it's detailed metadata by default. You can optionally specify a Code instead of an ID

    HTTP Request

    GET http://crm.oxifresh.com/locations/<ID>

    Or:

    GET http://crm.oxifresh.com/locations?code=<code>

    Query Parameters

    Parameter Description
    ID The ID of the location to retrieve
    code The Code of a location to retrieve

    Response

    Parameter Description
    location The location data including it's metadata

    Add A Location

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/locations');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'code' => "XX-0501F",
        'label' => 'Location of the Greater Area',
        'franchise_id' => 501,
        'is_default' => 1
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    var data = {
        'code' :  "XX-0501F",
        'label' :  'Location of the Greater Area',
        'franchise_id' :  501,
        'is_default' :  1
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/locations",
        method: "POST",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "location": {
            "id": 2060,
            "code": "XX-0501F",
            "label": "Location of the Greater Area",
            "franchise_id": 501,
            "is_default": true,
            "phone": "",
            "last_modified": "2016-10-06T19:22:16+00:00",
            "active": true,
            "full_name": "XX-0501A - Location of the Greater Area"
        }
    }
    

    This endpoint adds a new location to the system.

    HTTP Request

    POST http://crm.oxifresh.com/locations

    Data Parameters

    Parameter Optional Description
    code No The short code name for the location
    label No The label for the location
    franchise_id No The ID of the Franchise for this location
    is_default Yes Indicates if this is the default location to use if a review or contact belongs to a franchise but doesn't match it's Location Zipcode's
    phone Yes An optional phone number to associate with this location

    Response

    Parameter Description
    location The new location data

    Delete a Location

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/locations/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/locations/delete/4",
        method: "DELETE"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "location": {
            "id": 2060,
            "code": "XX-0501F",
            "label": "Location of the Greater Area",
            "franchise_id": 501,
            "is_default": true,
            "phone": "",
            "last_modified": "2016-10-06T19:22:16+00:00",
            "active": false,
            "full_name": "XX-0501A - Location of the Greater Area"
        }
    }
    

    This endpoint deletes a specific location by ID. Note that it's a "soft delete" - marking the active flag false. This removes it from GET lists and aggregate locations.

    HTTP Request

    DELETE http://crm.oxifresh.com/locations/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the location to delete

    Response

    Parameter Description
    location The "deleted" location data

    List Location Zipcodes

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 501,
        'code' => 'OF-0501A'
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/locationsZipcodes?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {
      'franchise_id' : 501,
      'code' : 'OF-0501A'
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/locationsZipcodes",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "locationsZipcodes": [
            {
                "id": 1,
                "location_id": 1,
                "display_location_id": 2,
                "zipcode": "80305",
                "active": true,
                "location": {
                    "id": 1,
                    "code": "OF-0501A",
                    "label": "Test A",
                    "franchise_id": 2,
                    "is_default": true,
                    "phone": null,
                    "last_modified": "2018-05-16T17:27:50+00:00",
                    "external_id": null,
                    "active": true,
                    "locations_metadata": null,
                    "full_name": "OF-0501A - Test A"
                },
                "display_location": {
                    "id": 2,
                    "code": "OF-0501B",
                    "label": "Test B",
                    "franchise_id": 2,
                    "is_default": false,
                    "phone": null,
                    "last_modified": "2018-05-16T17:28:02+00:00",
                    "external_id": null,
                    "active": true,
                    "locations_metadata": null,
                    "full_name": "OF-0002B - Test B"
                }
            },
            {
                "id": 2,
                "location_id": 1,
                "display_location_id": null,
                "zipcode": "80301",
                "active": true,
                "location": {
                    "id": 1,
                    "code": "OF-0501A",
                    "label": "Test A",
                    "franchise_id": 2,
                    "is_default": true,
                    "phone": null,
                    "last_modified": "2018-05-16T17:27:50+00:00",
                    "external_id": null,
                    "active": true,
                    "locations_metadata": null,
                    "full_name": "OF-0501A - Test A"
                },
                "display_location": null
            }
        ]
    }
    

    This endpoint a paginated list of Locations Zipcodes. You can search for a Location by it's ID explicitly, ex:

    https://crm.oxifresh.com/locationsZipcodes/1

    .. or you can search for one by it's Code (ex. https://crm.oxifresh.com/locationsZipcodes/OF-0501A). This will return partial matches, so you should include franchise_id with the search to eliminate most partial match issues.

    HTTP Request

    GET http://crm.oxifresh.com/locationsZipcodes

    Query Parameters

    Parameter Default Description
    code null Returns all Location Zipcodes which match the specified Location code
    zipcode null Returns a single Location Zipcode that matches the zipcode provided

    Response

    Parameter Description
    locationsZipcodes A paginated list of locations zipcodes, with their nested Location entity. Some may also list a "display location" - which should be used for any public-facing listings for a zipcode, as they may technically be listed internally under one Location but externally to customers are sorted by this "display" location

    Postal Codes

    Allows various lookups for Postal Code/Zipcode listings, such as Owned and Zones lists.

    Get Zone by Zipcode

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 501
    ];
    
    // For Countries with prefixed Postal Codes (CA), you can also list the n-digit prefix, eg. "V6C"
    $postalCode = "00000"; 
    
    $fieldsString = http_build_query($data);
    
    
    curl_setopt($curl, CURLOPT_URL, "https://crm.oxifresh.com/postalCodes/getZoneForPostalCode/$postalCode?$fieldsString");            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {
      'franchise_id' : 501
    }
    
    // For Countries with prefixed Postal Codes (CA), you can also list the n-digit prefix, eg. "V6C"
    var postalCode = '00000';
    baseRequest({
        url: "https://crm.oxifresh.com/api/postalCodes/getZoneForPostalCode/" + postalCode + "",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "zone": {
            "id": 58653,
            "zip_group_id": 2,
            "franchise_id": 501,
            "name": "Zone 1",
            "color": "FF99CC",
            "default_group": false,
            "_matchingData": {
                "ZipGroupsZipcodes": {
                    "id": 36950,
                    "zip_group_id": 2,
                    "zip_group_sub_group_id": 58653,
                    "franchise_id": 501,
                    "zipcode": "00000"
                }
            }
        }
    }
    

    This endpoint gets a Zone for a specific Address ID. Note that you must supply the Franchise ID as multiple Franchises may all list this Zipcode in their Zones (they do not have to be Owned by them to service that area).

    HTTP Request

    GET http://crm.oxifresh.com/postalCodes/getZoneForPostalCode/<postal_code>

    Query Parameters

    No extra query parameters

    Response

    Parameter Description
    zone A Zip Group Sub Group listed under the Zone group, which contains the Postal Code

    Get Zone By Address ID

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, "https://crm.oxifresh.com/postalCodes/getZoneByAddressId/11");            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/postalCodes/getZoneByAddressId/11",
        method: "GET"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "zone": {
            "id": 1642,
            "zip_group_id": 2,
            "franchise_id": 501,
            "name": "Zone 2",
            "color": "339966",
            "url": "",
            "location_label": null,
            "default_group": null,
            "_matchingData": {
                "ZipGroupsZipcodes": {
                    "id": 1579606,
                    "zip_group_id": 2,
                    "zip_group_sub_group_id": 1642,
                    "franchise_id": 501,
                    "zipcode": "V1X"
                }
            }
        },
        "address": {
            "id": 680448,
            "entity_type": 4,
            "entity_id": 658019,
            "template_id": 1,
            "type_id": 1,
            "template_group_id": 0,
            "address1": "1725 Fake St.",
            "address2": "",
            "unit_type": 1,
            "unit_number": "3",
            "county": "",
            "city": "Kelowna",
            "state": "BC",
            "zipcode": "V1X7H1",
            "country_id": 2,
            "active": 1,
            "geocode": "49.8864164,-119.4211188",
            "subset": "",
            "country": {
                "id": 2,
                "code": "CA",
                "name": "CANADA",
                "allow_postal_code_wildcards": true,
                "minimum_postal_code_digits": 3,
                "active": 1
            }
        }
    }
    

    This endpoint gets a Zone for a specific Address ID. Note that the Franchise account will be automatically determined by the linked Entity specified in the Addresss's entity_type/entity_id pair.

    Your API user must have access to that Franchise account to retrieve the Zone.

    HTTP Request

    GET http://crm.oxifresh.com/postalCodes/getZoneForPostalCode/<postal_code>

    Query Parameters

    No extra query parameters

    Response

    Parameter Description
    zone A Zip Group Sub Group listed under the Zone group, which contains the Postal Code
    address The matching Address's information, along with nested Country information

    Reports

    Our newest reports system offers API-compatible data formats that can be used for more than just polling for data to show to users.

    Several reports actually assist in aggregating data in near-real-time into pre-computed data sets, aggregating job data in practical, cross-franchise data sets that vastly simplify tasks like querying for lists of unique marketable emails or addresses.

    Marketing

    Households

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 501,
        'last_modified.after' => "2019-02-27 18:19:00"
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/marketingLists/households?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {franchsie_id: 501, "last_modified.after": "2019-02-27 18:19:00"}
    baseRequest({
        url: "https://192.168.50.4/reports/marketingLists/households",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "households": [
            {
                "_id": {
                    "$oid": "5c781c4707612912a535b752"
                },
                "address1": "300 Man Dr.",
                "address2": "",
                "unit_number": "300",
                "county": null,
                "city": "Boulder",
                "state": "CO",
                "zipcode": "80303",
                "country_id": 1,
                "active": true,
                "date_created": "2019-02-28T17:37:11+00:00",
                "most_recent": {
                    "franchise": {
                        "id": 501
                    },
                    "invoice": {
                        "id": 123456,
                        "franchise_id": 2,
                        "date_completed": "2019\/02\/28 09:00:00"
                    },
                    "contact": {
                        "id": 789456,
                        "franchise_id": 2,
                        "email": "sample@oxifresh.com"
                    }
                },
                "last_modified": "2019-02-28T18:34:20+00:00"
            }
        ],
        "total": 1,
        "limit": 100,
        "page": 1,
        "match": {
            "last_modified": {
                "$gte": {
                    "$date": {
                        "$numberLong": "1551287940000"
                    }
                }
            }
        }
    }
    
    

    This report returns a list of unique addresses and the most recent job & contact data for each. It can be optionally limited to specific franchises, a specific service window (with most_recent.invoice.date_completed.after and most_recent.invoice.date_completed.before), or just the most recent changes since a specified date (with last_modified.after)

    This is well suited to pulling lists of marketable addresses for "Last Serviced n-months ago" type mailing campaigns.

    HTTP Request

    GET http://crm.oxifresh.com/locations

    Query Parameters

    Parameter Default Description
    last_modified.after null Specify a date to only retrieve household records updated after that date
    most_recent.invoice.date_completed.after null Specify a date to only retrieve records with jobs completed after that date
    most_recent.invoice.date_completed.before null Specify a date to only retrieve records with jobs completed before that date

    Response

    Parameter Description
    households A paginated list of unique addresses, which includes all the available most recent basic Franchise, Invoice, and Contact information
    match The parsed criteria used in the search
    total The total number of unique addresses matching the input criteria

    Emails

    <?php
    $curl                 = curl_init();
    
    $data = [
        'franchise_id' => 501,
        'most_recent.invoice.date_completed.after' => "2018-10-01 00:00:00",
        'most_recent.invoice.date_completed.before' => "2018-11-01 00:00:00"
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/emails?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    var data = {
      franchsie_id: 501,
       "most_recent.invoice.date_completed.after": "2018-10-01 00:00:00",
       "most_recent.invoice.date_completed.before": "2018-11-01 00:00:00",
    }
    baseRequest({
        url: "https://192.168.50.4/reports/marketingLists/emails",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    
    

    This will return JSON results structured like this:

    {
        "marketingEmails": [
            {
                "_id": {
                    "$oid": "5c7829a4076129180b322dc2"
                },
                "email": "ahoffner@gmail.com",
                "is_unsubscribed": false,
                "unsubscribed_on_date": null,
                "most_recent": {
                    "franchise": {
                        "id": 501
                    },
                    "invoice": {
                        "id": 773786,
                        "franchise_id": 2,
                        "date_completed": "2019\/02\/28 09:00:00"
                    },
                    "address": {
                        "address1": "300 Man Dr.",
                        "address2": "",
                        "unit_number": "",
                        "county": null,
                        "city": "Boulder",
                        "state": "CO",
                        "zipcode": "80303",
                        "country_id": 1
                    }
                },
                "date_created": "2019-02-28T18:34:12+00:00",
                "last_modified": "2019-02-28T18:34:18+00:00"
            }
        ],
        "total": 1,
        "limit": 100,
        "page": 1,
        "match": {
            "last_modified": {
                "$gte": {
                    "$date": {
                        "$numberLong": "1551287940000"
                    }
                }
            }
        }
    }
    
    

    This report is generally identical to Households, but it instead returns a list of unique emails and the most recent job & contact data for each. It can also be filtered similarly, searchable by franchise ID, last job date, the date last modified.

    It is similarly well suited to pulling lists of marketable emails for "Last Serviced n-months ago" type email campaigns.

    HTTP Request

    GET http://crm.oxifresh.com/locations

    Query Parameters

    Parameter Default Description
    last_modified.after null Specify a date to only retrieve household records updated after that date
    most_recent.invoice.date_completed.after null Specify a date to only retrieve records with jobs completed after that date
    most_recent.invoice.date_completed.before null Specify a date to only retrieve records with jobs completed before that date

    Response

    Parameter Description
    marketing_emails A paginated list of unique addresses, which includes all the available most recent basic Franchise, Invoice, and Contact information
    match The parsed criteria used in the search
    total The total number of unique addresses matching the input criteria

    Reviews

    Get a list of reviews

    <?php
    $curl                 = curl_init();
    
    $data = [
        'location' => [
            'code' => '2A'
        ]
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/reviews?'.$fieldsString);            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    foreach($response['reviews'] as $review){
        echo "<p>$review->comment<br>(Rated $review->rating / 10)</p>";
    }
    
    
    var data = {
        'location' : {
            'code' : '2A'
        }
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/reviews",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "reviews": [
            {
                "id": 1,
                "franchise_id": 501,
                "reviews_source_id": 1,
                "comment": "This was great",
                "rating": 9,
                "allowed_public": 1,
                "censored": 0,
                "zipcode": "80305",
                "date_reviewed": "2016-09-21T15:52:35+0000",
                "location_id": 4,
                "invoice_id": 146745,
                "contact_id": 230143,
                "date_created": "2016-09-22T16:54:23+0000",
                "last_modified": "2016-09-27T20:56:20+0000",
                "active": true,
                "invoice": {
                    "id": 146745,
                    "franchise_id": 501,
                    "invoice_status_id": 4,
                    "contact_id": 230143,
                    "employee_id": 1730,
                    "work_address_id": 228878,
                    "campaign_id": null,
                    "sequence": 6998,
                    "po_number": null,
                    "created_by_id": 2050,
                    "date_created": "2011-09-21T09:13:11+00:00",
                    "last_modified": "2015-02-27T04:06:21+00:00",
                    "date_due": "2011-09-26T00:00:00+00:00",
                    "date_quoted": "2011-09-26T00:00:00+00:00",
                    "date_completed": "2011-09-26T00:00:00+00:00",
                    "date_paid": "2011-09-30T00:00:00+00:00",
                    "notes": "",
                    "subtotal": 131,
                    "tax_percent": 0.087,
                    "tax_due": 0,
                    "amount_due": 131,
                    "amount_paid": 131,
                    "original_subtotal": 146,
                    "quote_confirmed": true,
                    "imported": null,
                    "active": true,
                    "address": {
                        "id": 228878,
                        "entity_type": 4,
                        "entity_id": 230143,
                        "template_id": 1,
                        "type_id": 1,
                        "template_group_id": null,
                        "address1": "123 Real St.",
                        "address2": null,
                        "unit_type": 1,
                        "unit_number": null,
                        "county": null,
                        "city": "WESTMINSTER",
                        "state": "CO",
                        "zipcode": "80031",
                        "country_id": 1,
                        "active": 1,
                        "geocode": "39.8981100,-105.0344940",
                        "subset": null
                    },
                    "summary": null
                },
                "location": {
                    "id": 1,
                    "code": "OF-0002A",
                    "label": "Oxi Fresh of Location Area",
                    "franchise_id": 2,
                    "is_default": false,
                    "phone": "303-555-0120",
                    "last_modified": "2016-10-10T22:28:56+00:00",
                    "active": true,
                    "full_name": "OF-0002A - Oxi Fresh of Location Area"
                },
                "contact": {
                    "id": 230143,
                    "franchise_id": 501,
                    "type_id": 1,
                    "classification_id": 1,
                    "status_id": 2,
                    "campaign_id": 0,
                    "title": "",
                    "first_name": "Real",
                    "middle_initial": "",
                    "last_name": "Customername",
                    "suffix": "",
                    "date_of_birth": null,
                    "gender": "",
                    "telephone": "3035551235",
                    "telephone2": "",
                    "telephone3": "",
                    "telephone4": "",
                    "email": "realaddress@oxifresh.com",
                    "url": "",
                    "account_number": "",
                    "company_name": "",
                    "date_created": "2011-09-21T00:00:00+00:00",
                    "last_modified": "2013-09-05T20:11:12+00:00",
                    "date_called": null,
                    "hide": false,
                    "active": false,
                    "unsubscribed": false,
                    "constant_contact_id": null
                },
                "reviews_source": {
                    "id": 2,
                    "lookup_key": "listen_360",
                    "name": "Listen360",
                    "is_default": false,
                    "active": true
                }
            },
             {
                "id": 2,
                "franchise_id": 501,
                "reviews_source_id": 2,
                "comment": "This was not as great",
                "rating": 2,
                "allowed_public": 0,
                "censored": 1,
                "zipcode": "80129",
                "date_reviewed": "2016-09-10T15:52:35+0000",
                "location_id": 4,
                "invoice_id": null,
                "contact_id": null,
                "date_created": "2016-09-22T16:54:23+0000",
                "last_modified": "2016-09-27T20:56:20+0000",
                "active": true,
                "location": {
                    "id": 2,
                    "code": "2A",
                    "label": "Location Of West Coast",
                    "franchise_id": 501,
                    "is_default": true,
                    "phone": "30354551234",
                    "active": true,
                    "full_name": "2B - Location Of West Coast"
                },
                "contact": null,
                "reviews_source": {
                    "id": 2,
                    "lookup_key": "listen360",
                    "name": "Listen360",
                    "is_default": false,
                    "active": true
                }
            }
        ]
    }
    

    This endpoint gets a paginated list of customer Reviews. Note that reviews are aggregated from multiple different sources.

    HTTP Request

    GET http://crm.oxifresh.com/reviews

    Query Parameters

    Parameter Default Description
    location null An array of location information to limit review results with. Examples include: Code (ex. 2B), Label (ex. Location of Denver)
    search null A generic search term that will return reviews with matching comments, ratings, review sources, and contact names
    showNotAllowedPublic false Set to true to include reviews that were not indicated to be shown publicly
    showCensored false Set to true to include reviews that have been "censored" for various reasons (profanity, trolling, etc.)

    Response

    Parameter Description
    reviews A paginated list of reviews, includes the relevant Location, Review Source, and if listed Contact and Invoice information.
    total The total number of reviews matching the input criteria

    Get a Specific Review

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/reviews/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/review/4",
        method: "GET"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "review": [
            {
                "id": 1,
                "franchise_id": 2,
                "reviews_source_id": 1,
                "comment": "This was great",
                "rating": 9,
                "allowed_public": 1,
                "censored": 0,
                "zipcode": "80305",
                "date_reviewed": "2016-09-21T15:52:35+0000",
                "location_id": 4,
                "invoice_id": null,
                "contact_id": null,
                "date_created": "2016-09-22T16:54:23+0000",
                "last_modified": "2016-09-27T20:56:20+0000",
                "active": true,
                "location": {
                    "id": 4,
                    "code": "2D",
                    "label": "Location Of Oz",
                    "franchise_id": 2,
                    "is_default": false,
                    "phone": null,
                    "active": true,
                    "full_name": "2D - Location Of Oz"
                },
                "contact": null,
                "reviews_source": {
                    "id": 1,
                    "lookup_key": "website",
                    "name": "Oxifresh.com",
                    "is_default": true,
                    "active": true
                }
            }
        ]
    }
    

    This endpoint retrieves a specific review by ID.

    HTTP Request

    GET http://crm.oxifresh.com/reviews/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the review to retrieve

    Response

    Parameter Description
    review The review data

    Add A Review

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/reviews');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $data = [
        'rating' => 9,
        'comments' => 'Service was great.',
        'contacts' => [
            'first_name' => 'Kaiden',
            'last_name' => 'Alenko',
        ],
        'addresses' => [
            'zipcode' => 80305    
        ]
    ];
    
    $fieldsString = http_build_query($data);
    
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $fieldsString );
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    var data = {
        'rating' :  9,
        'comments' :  'Service was great.',
        'contacts' :  {
           'first_name' :  'Kaiden',
           'last_name' :  'Alenko',
        },
        'addresses' :  {
            'zipcode' :  80305    
        }
    }
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/reviews",
        method: "POST",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "review": {
            "id": 15,
            "franchise_id": 2,
            "reviews_source_id": 1,
            "comment": null,
            "rating": 8,
            "allowed_public": 1,
            "censored": 0,
            "zipcode": "80111",
            "date_reviewed": "2016-09-28T19:34:47+0000",
            "location_id": 2,
            "invoice_id": null,
            "contact_id": 9163,
            "date_created": "2016-09-28T19:34:47+0000",
            "last_modified": "2016-09-28T19:34:47+0000",
            "active": true,
            "invoice": null,
            "location": {
                "id": 2,
                "code": "2B",
                "label": "Location Of Whereever",
                "franchise_id": 2,
                "is_default": false,
                "phone": null,
                "active": true,
                "full_name": "2B - Location Of Whereever"
            },
            "contact": {
                "id": 9163,
                "franchise_id": 2,
                "type_id": 1,
                "classification_id": 1,
                "status_id": 2,
                "campaign_id": 0,
                "title": null,
                "first_name": "Andy",
                "middle_initial": null,
                "last_name": "Horner",
                "suffix": null,
                "date_of_birth": null,
                "gender": "U",
                "telephone": "3037791389",
                "telephone2": "3039467226",
                "telephone3": "",
                "telephone4": null,
                "email": null,
                "url": null,
                "account_number": null,
                "company_name": "Andy Horner",
                "date_created": "2009-09-09T00:00:00+0000",
                "last_modified": "2010-02-26T04:23:53+0000",
                "date_called": "2009-09-09T00:00:00+0000",
                "hide": false,
                "active": true,
                "unsubscribed": false,
                "constant_contact_id": null,
                "full_name": "Andy Horner"
            },
            "reviews_source": {
                "id": 1,
                "lookup_key": "website",
                "name": "Oxifresh.com",
                "is_default": true,
                "active": true
            }
        }
    }
    

    This endpoint adds a new review to the system.

    Note it is recommended you supply at least:

    If you supply these data points along with the Review, we can sort the review into the proper Location (ex. 2A - Oxi Fresh of Denver, and implicitly the proper Franchise #2) and also attempt to match the review with a specific Contact in our system.

    HTTP Request

    POST http://crm.oxifresh.com/reviews

    Data Parameters

    Parameter Optional Default Description
    rating* No null A numeric rating from 0 - 10 (worst to best, respectively)
    comment* Yes null The comment text
    contacts* Yes null Can contain an array of all known Contact information (first_name, last_name, email, telephone, for example) which will be used to make a best-guess match for who the review is in our database
    addresses* Yes null Can contain an array of all known Address information (zipcode, address_1, city, state, etc.) which will be used both to help refine the best-guess match for the Contact, and the zipcode is also used to sort into the proper Location automatically.
    allowed_public Yes true Set to false to indicate the reviewer did not want the review made public
    censored Yes false Set to true to indicate in advance if the review should be "censored" (note it may be marked censored automatically based on comments contents).
    date_reviewed Yes Now() Defaults to the current time, but optionally can specify a different date/time string (YYYY-MM-DD HH:ii:ss) if the review was provided at a different point in time.
    review_source_id Yes Varies The ReviewsSource ID, defaults to a configured source, usually "website". Query GET \reviewsSources to view a list of possible sources.
    contact_id Yes null If known, you can supply the exact Contact ID for the review
    invoice_id Yes null If known, you can supply the exact Invoice ID for the review
    location_id Yes null If known, you can supply the exact Location ID for the review

    *recommended fields

    Response

    Parameter Description
    review The new review data, including any matched extended information (Contact, Invoice, Location, ReviewsSource

    Delete a Review

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/reviews/4');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/reviews/4",
        method: "DELETE"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "review": [
            {
                "id": 1,
                "franchise_id": 2,
                "reviews_source_id": 1,
                "comment": "This was great",
                "rating": 9,
                "allowed_public": 1,
                "censored": 0,
                "zipcode": "80305",
                "date_reviewed": "2016-09-21T15:52:35+0000",
                "location_id": 4,
                "invoice_id": null,
                "contact_id": null,
                "date_created": "2016-09-22T16:54:23+0000",
                "last_modified": "2016-09-27T20:56:20+0000",
                "active": false
            }
        ]
    }
    

    This endpoint deletes a specific review by ID. Note that it's a "soft delete" - marking the active flag false. This removes it from GET lists and aggregate reviews.

    HTTP Request

    DELETE http://crm.oxifresh.com/reviews/<ID>

    Query Parameters

    Parameter Description
    ID The ID of the review to delete

    Response

    Parameter Description
    review The "deleted" review data

    Get Review Averages

    <?php
    $curl                 = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'https://crm.oxifresh.com/reviews/average/2B');            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    
    curl_close($curl);
    
    echo $response;
    
    
    baseRequest({
        url: "https://crm.oxifresh.com/api/reviews/average/2B",
        method: "GET"
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "average_rating": 8.7,
        "total": 72
    }
    

    This endpoint returns the average rating and total number of ratings, either overall or for the specified Location Code.

    HTTP Request

    GET http://crm.oxifresh.com/reviews/average/<location_code>

    Query Parameters

    Parameter Description
    location_code Optionally a Location code (ex. 2B) to restrict results to.

    Response

    Parameter Description
    average_rating The average rating down to one decimal point
    total The total number of matching ratings

    retrieves a specific review by ID.

    Tax Rates

    Enables lookup of tax rates for a given postal code, with up to rooftop-level accuracy when given the full address.

    Get Zone by postal code

    <?php
    $curl                 = curl_init();
    
    $postalCode = '80228';
    
    curl_setopt($curl, CURLOPT_URL, "https://crm.oxifresh.com/taxRates/$postalCode");            
    curl_setopt($curl, CURLOPT_USERPWD, 'api_username:api_password');
    
    $response = json_decode(curl_exec($curl), true);
    $info   = curl_getinfo($curl);
    curl_close($curl);
    
    if($info['http_code'] != '200') {
        throw new Exception('HTTP Request Exception: '.print_r($info,1));
    }
    
    
    
    // For Countries with prefixed Postal Codes (CA), you can also list the n-digit prefix, eg. "V6C"
    var postalCode = '80228';
    baseRequest({
        url: "https://crm.oxifresh.com/taxRates/" + postalCode + "",
        method: "GET",
        body: data
      },
      function (error, response, body) {
        if (!error && response.statusCode == 200) {
          // Success!
        }
      }
    )
    

    This will return JSON results structured like this:

    {
        "taxRate": {
            "rate": "0.045",
            "postal_code": "80228",
            "city": "LAKEWOOD",
            "state": "CO",
            "county": "JEFFERSON",
            "expiration_date": "2019-09-05T16:42:43-06:00"
        }
    }
    

    This endpoint returns the best-match tax rate for the postal code provided. It returns the sales tax rate, what city/state/county matched for the postal code & other address information provided, and also the date that rate will expire from caches (typically a set number of days since last querying for the rate, with a hard stop at the end of each year).

    Note that most rates are determined by a 3rd party company which specializes in taxes, but each postal code & each franchisee may override their recommendations on a case-by-case basis. This endpoint will return the overridden rates if on-file agnostically. As the output JSON looks identical, you don't need to handle overriden rates differently.

    HTTP Request

    GET http://crm.oxifresh.com/taxRates/<postal_code>

    Query Parameters

    All additional query parameters listed here are optional, but can improve accuracy.

    Parameter Description
    city The city for the sale(RECOMMENDED - vastly improves accuracy, in the US particularly)
    state The state for the sale (Use 'state' even for Countries that label these differently, like Canada's 'Provinces')
    county The county for the sale
    address1 The street address for the sale
    address2 The 2nd line address (Apt or unit #) for the sale

    Response

    Parameter Description
    rate The sales tax rate for the location. Note that only services flagged as "taxable" should apply this rate in sales
    postal_code The postal code specified, for the rate returned
    city The city for the rate returned. May not match input City if it was deemed invalid by the tax rate provider internally (it will return a best-guess city for the postal codeinstead)
    state The State for the rate returned. Like City, may not match input State if it was deemed invalid
    county The County for the rate returned. Like City, may not match input County if it was deemed invalid

    Push API

    In addition to our normal API endpoints, in which you can request data on-demand, we also have a Push API that allows you to subscribe to one or more changes to certain entities in near-real-time.

    Supported Entities

    We currently can forward changes to the following:

    Setup

    The simplest way is to create a new subscription in the Push API section of the Scheduling Center, found under the Other umbrella: http://crm.oxifresh.com/pushApiEndpoints

    Creating A New Endpoint

    When creating a new endpoint, you supply the URL data will be POST'ed to. Optionally you can include an API Key in the URL itself.

    We can forward data in the same JSON format used in regular API responses, or optionally in a Key-Value format used by browsers when submitting forms.

    Select what entity this endpoint will monitor, and if you wan to receive all change (Adds, Edits, Deletes) - or only a specific action.

    Release Notes

    Below are any relevant changes to the Scheduling Center API, including migration guides for breaking changes.

    v1.6.1 Hotfix for Tax Rates API

    Released on 8/4/2019

    This release primarily added a new Tax Rates API & also added City to the New Customer workflow to improve tax rate accuracy overall.

    New Features

    v1.6 Short Stack

    Released on 3/7/2019

    This release added support for Multi-Franchise Owners and Techs - a feature that allows an Employee to belong to more than one Franchise at a time.

    It also will begin to (softly) enforce emails uniqueness on every Employee account. Users will be prompted to verify ownership of their emails if they have not already done so, and also to change their email (or request it removed from other accounts) if it's listed on multiple accounts.

    It also contains background tools that better support SSO in general, including our Naranga integration, and updates Listen360's SSO integration to bring it inline with newer conventions.

    New Features

    Migration

    Most of the changes are backwards compatible, however there are some notable changes to Employees & to deactivated entities in general:

    Changes for Employees Franchises

    Employees no longer have a single Franchise ID field listed on the entity. The new potential one-to-many relationship has necessitated the addition of a new EmployeesFranchises table that stores a list of all the Franchises each Employee has access to.

    To assist with migration, the existing Franchise ID field was left on each employee for now, but it has been renamed to deprecated_franchise_id.

    Stricter Active Flag Conventions

    Some improvements and bug fixes to the central pieces that filter out soft-deleted entities (by marking their active flags false) will more strictly filter out these de-activated records.

    If your API has been coded to rely on inactive entities being errantly included in index lists or in GET endpoints, you may need to make modifications. All documented index lists now support the showInactive that will include deactivated results in the listings (the legacy named showHidden is still supported for now as well, until v1.7).

    v1.5 Short Stack

    Released on 12/19/2018

    This release primarily included a suite of new background processing systems that could synchronize data changes (like Employees and Franchises) from the Scheduling Center to relevant 3rd parties.

    This automated provisioning and updates system was required for a number of Single Sign Ons to work seamlessly, or improved functionality for some existing ones.

    It also enabled our Push API, which can forward changes to a number of entities in near-real-time to interested parties.

    It also contains alpha-versions of two upcoming features, Households & Marketing Emails, two systems which compile job data in near-real-time into rows of unique addresses and emails (respectively) listing the most recent service dates, average review scores, and most recent invoice IDs. These system will go live in the next release, v1.6.

    Migration

    Few major changes were made to existing features nor were any fields removed. Likely your integration will remain largely backwards compatible.

    You may need to check Location status which will indicate if it's ready to be published or still in draft state, indicating its still under construction internally.

    Entity Changes

    A couple of tables added creation & modification fields: - Employees - SingleSignOnsIds

    Added
    date_created
    created_by_employee_id
    last_modified
    last_modified_by_employee_id
    Added Desc
    status A string who can be draft, published, or hidden. Location will not be propagated to 3rd parties if it's still in draft status
    Added Desc
    geo_modifier A string representation of the address location (ex. "South Denver")

    v1.4.7

    This release standardized several field names in Todos and Billing, as well as cleaned up some deprecated fields in Franchises.

    Removed Fields

    Removed
    address1
    address2
    city
    state
    zipcode
    fax
    short_emails
    long_emails

    Addresses have internally already been tracked using the address_id association, this removes them from API output to avoid confusion.

    The two email fields, short_emails and long_emails were also deprecated internally for some time. To get lists of a franchise's Owner's emails, query the Employees API endpoint for those in the "Franchise Owners" Group.

    Renamed Fields

    Old Field Name New Name
    created_by_id created_by_employee_id
    last_modified_by_id last_modified_by_employee_id

    Note these are now tracked automatically and cannot be set manually. 'data_exports_file_id'

    To-Be-Documented

    The following Controllers generally support the default actions (GET, POST, PUT, PATCH, DELETE), but no additional documentation has been written for them at this time.

    Note that there may be endpoints accessible in addition to those listed here, however they usually are from Legacy packages which rarely follow the conventions of our newer modules and require additional domain knowledge to use.

    Campaigns Sub-Categories

    Concerns the top-level Categories marketing campaigns are sorted into.

    HTTP Request

    http://crm.oxifresh.com/campaignsSubcategories

    Coupons

    A coupon is a (usually complex) discount set which contains rules for applicability & discount rates depending on how the rules are satisfied.

    HTTP Request

    http://crm.oxifresh.com/coupons

    Coupons Packs

    A group of re-usable Coupons. Can be listed on any number of marketing Campaigns.

    HTTP Request

    http://crm.oxifresh.com/couponsPacks

    Coupons Rules

    A single discount rate & a potentially a list of Coupons Rules Items to qualify for that rate. A single Coupon may list multiple Coupons Rules to accomplish complex discount situations.

    HTTP Request

    http://crm.oxifresh.com/couponsRules

    Data Exports

    A record of a a data export job & it's status.

    HTTP Request

    http://crm.oxifresh.com/dataExports

    Data Exports Files

    The actual completed files exported out (usually CSV or XML files)

    HTTP Request

    http://crm.oxifresh.com/dataExportsFiles

    Data Exports Formats

    Predefined, re-usable formats to export data in once or routinely.

    HTTP Request

    http://crm.oxifresh.com/dataExportsFormats

    Data Exports Formats Fields

    The individual data fields, constants, or special calculations to include in a given Data Export Format

    HTTP Request

    http://crm.oxifresh.com/dataExportsFormatsFields

    Data Exports Logs

    Entity-level records of an export activity for auditing purposes.

    HTTP Request

    http://crm.oxifresh.com/dataExportsLogs

    Data Exports Logs

    Entity-level records of an export activity for auditing purposes.

    HTTP Request

    http://crm.oxifresh.com/dataExportsLogs

    Data Exports Uploaders

    Locations / information on where to send exported data when scheduled

    HTTP Request

    http://crm.oxifresh.com/dataExportsUploaders

    Data Exports Uploads Schedules

    Scheduled uploads of pre-defined Export Formats to predefined Uploaders

    HTTP Request

    http://crm.oxifresh.com/dataExportsUploaders

    Employees

    Concerns the Employees (users) that can login into the system, and information about the user.

    HTTP Request

    http://crm.oxifresh.com/employees

    Equipment

    Concerns the Equipment that can be assigned to Employees on the Schedule, which determines what types of Service's Categories they can perform on what days.

    HTTP Request

    http://crm.oxifresh.com/equipment

    Franchises

    Lists the Franchises accounts. Supports only GET, POST /activate, and DELETE /deactivate.

    HTTP Request

    http://crm.oxifresh.com/campaignsSubcategories

    Groups

    Concerns the Employee Groups, as well as the set of Permissions listed in each Group.

    HTTP Request

    http://crm.oxifresh.com/groups

    Help Information

    A single record containing a phone, email, and Usersnap API key for users to get help with.

    HTTP Request

    http://crm.oxifresh.com/helpInformation

    Landing / What's New

    Can provide HTML from the GET action - containing a page describing the latest new features in the SchedulingCenter.

    HTTP Request

    http://crm.oxifresh.com/landing http://crm.oxifresh.com/whatsNew

    Line Items

    Can return a list of an invoices line items with GET, or a list of services/products that are able to be added to and invoice with GET /list

    HTTP Request

    http://crm.oxifresh.com/lineItems

    Locations Zipcodes

    Concerns the Zipcodes assigned to Locations. Zipcodes-Locations are one-to-one.

    Also implements: * exists - to check for existing zipcode-location assignments * reassign - to move a Zipcode to a different location_id * saveList - to save a bulk comma-separated list of zipcodes to a location_id

    HTTP Request

    http://crm.oxifresh.com/locationsZipcodes

    Message Of The Day

    Concerns the banner-style messages visible at the top of the page.

    HTTP Request

    http://crm.oxifresh.com/messageOfTheDay

    Message Of The Day Logs

    Concerns the log of who has viewed each of Message of the Day messages.

    HTTP Request

    http://crm.oxifresh.com/messageOfTheDayLogs

    Online Scheduling Services

    Concerns the list of services available via Online Scheduling.

    HTTP Request

    http://crm.oxifresh.com/onlineSchedulingServices

    Online Scheduling Service Categories

    Concernss the categories the Online Scheduling Services are grouped under.

    HTTP Request

    http://crm.oxifresh.com/onlineSchedulingServicesCategories

    Popups

    Concerns the popup-style messages attached to Contacts visible when pulling up their information in the Calendar

    HTTP Request

    http://crm.oxifresh.com/popups

    Popups Logs

    Concerns the log of who viewed each of the popup-style messages.

    HTTP Request

    http://crm.oxifresh.com/popupsViewLog

    Punch Clock

    Concerns the punch clock times used to record when Employees have started / ended working. Includes a punchTheClock method which toggles the punched in / punched out status of the logged in user (you)

    HTTP Request

    http://crm.oxifresh.com/punchClock

    Releases

    Concerns the information about the Scheduling Center's software releases, for informational purposes.

    HTTP Request

    http://crm.oxifresh.com/releases

    Scheduling

    Concerns the Employee's Schedule (times available) and Assignments (what Equipment or service Categories they can work).

    Does not support JSON requests at this time, and only the GET and calendar actions.

    HTTP Request

    http://crm.oxifresh.com/scheduling

    Scheduling Modules

    Concerns the list of "modules" that can impact an Employee's scheduling (ex. Franchise Hours, Employee Hours, Zone Assignments, etc.)

    HTTP Request

    http://crm.oxifresh.com/schedulingModules

    Scheduling Module Preferences

    Concerns which of the "optional" Scheduling Modules each Franchisee has elected to use (ex. Category Assignments instead of Equipment Assignments). Only supports PATCH and DELETE.

    HTTP Request

    http://crm.oxifresh.com/schedulingModulesPreferences

    Scheduling Times - Custom Dates

    Concerns the one-off, specific date entries for time-based Scheduling modules (ex. Franchise & Employee hours)

    HTTP Request

    http://crm.oxifresh.com/schedulingTimesCustomDates

    Scheduling Times - Default

    Concerns the default week-to-week scheduling entries for time-based Scheduling modules (ex. Franchise & Employee hours)

    HTTP Request

    http://crm.oxifresh.com/schedulingTimesDefaultWeekly

    Scheduling Assignments - Custom Dates

    Concerns the one-off, specific date entries for assignment-based Scheduling modules (ex. Zones, Equipment, Categories)

    HTTP Request

    http://crm.oxifresh.com/schedulingAssignmentsCustomDates

    Scheduling Assignments - Default

    Concerns the default week-to-week scheduling entries for assignment-based Scheduling modules (ex. Zones, Equipment, Categories)

    HTTP Request

    http://crm.oxifresh.com/schedulingAssignmentsDefaultWeekly

    Services

    The app-level list of Service items that all Franchises can potentially use.

    HTTP Request

    http://crm.oxifresh.com/services

    Services Settings

    The franchisee-specific prices and durations for each app-level Service. These IDs are whats listed in Invoice Line Items service_id

    HTTP Request

    http://crm.oxifresh.com/servicesSettings

    Single Sign On

    Concerns the Signle Sign On modules available for Employees to utilize in Scheduling Center

    HTTP Request

    http://crm.oxifresh.com/singleSignOns

    Todos

    Concerns the list of Todos assigned to specific Employees (or to the entire Franchise)

    HTTP Request

    http://crm.oxifresh.com/todos