Salesforce, Python, SQL, & other ways to put your data where you need it

Need event music? 🎸

Live and recorded jazz, pop, and meditative music for your virtual conference / Zoom wedding / yoga class / private party with quality sound and a smooth technical experience

WCOnline API explainer

20 Oct 2021 🔖 api integration python
💬 EN

Table of Contents

Apparently at many schools, Twenty Six Design’s WCOnline cloud software is a popular choice for online tutoring and for making appointments with tutors available through the system.

Its appointment-scheduling data can be downloaded, a little bit at a time, over an HTTP-based API, which you can learn more about public documentation and privately at:

https://YOUR_SCHOOL_NAME.mywconline.net/f_admin.php?type=GLOBAL_INTEGRATE

Configuration

To set an API key, you go into this site:

https://YOUR_SCHOOL_NAME.mywconline.net/f_admin.php?type=GLOBAL_INTEGRATE

And you literally type in any API key of your choosing. There isn’t a “generate a key for me” button. GRC’s Ultra High Security Password Generator.

At the time of writing this, the System Integrations configuration site was perfectly willing to just show you the API key you chose upon subsequent visits, so definitely don’t put something like my_usual_database_sysadmin_password here.

And after you’re all done testing on your local machine, as with all APIs, go in and change it. Which with this case, will just mean generating some new long, random text and pasting it and hitting “save changes.”

You also have to give WCOnline exactly one public IPv4 address from which you’ll be making API requests.

If you hit an API endpoint from a different IP address, you’ll get a 200 (OK) response with a blank body, not a response telling you you messed up.

You can change the IP address as often as you’d like, so go ahead and make it your local machine so you can easily test an endpoint with Postman, Python, or another favorite way of testing HTTP requests.

Usage

Timestamp

Each time you make a request, you need to figure out what time it is in UTC-5, which is U.S. Eastern Standard Time (even in the summer – don’t use Eastern Daylight Time), which corresponds to the timezone settings of WCOnline’s API server.

If that suddenly doesn’t work, go back into the web admin portal & make sure it didn’t change. It’ll tell you what time the system thought it was when you loaded the admin portal URL.

Then you’ll have to format the time in a specific way into plaintext.

WCOnline doesn’t make it obvious what the “specific way” in which you have to format the current time is – they just give you a code example and hope you’ll recognize what programming language it is, and therefore what date-formatting code it uses.

The example seems to be in PHP, based on concatenating with periods.

All dates/months/hours have leading zeroes.

The hour is on 24-hour time (00-23).

MD5 hash

Then you concatenate this datestamp to the API key you set, concatenate in the public IP address of the machine from which you’re doing the HTTP request, and then compute an MD5 hash around this concatenated mega-value.

Adjust accordingly for the programming language with which you’ll actually be making an HTTP request.

URL parameters

The MD5 hash goes into the URL of each GET-typed HTTP request you make to the API as a key URL parameter, along with a date parameter indicating which day’s data you’ll want to download (YYYYMMDD format w/ leading zeroes) and a type parameter indicating what type of data you’d like to download.

Response gotchas

The HTTP response is often a 200 (OK) response – even bad requests are just 200 with a blank body, a lot of the time.

There simply isn’t a lot of differentiation between valid requests with responses that simply have no data and responses to invalid requests (like your IP address being misconfigured).

Limitations

There’s not much in WCOnline’s API – just a few read-only appointment-related endpoints.

Furthermore:

  1. API calls can only return 1 day’s worth of data at a time
  2. You can only do 10 of them per hour.
  3. The way you prove you’re “legit” to the API is possibly prone to failure for 24 brief periods a day. I think it might fail if you compute a timestamp immediately before the hour of the day flips from X:59:59 to (X+1):00:00. I suppose that depends how robustly they coded the back-end to behave for the first few seconds after the time of day flips hours.
    • (I think they might have coded for that, actually, because at first I misread things and computed central standard time and it worked … then I switched to computing eastern standard time and … it also worked.)

Request-response examples

Appointments

An “yesterday’s apointments” request to this URL:

https://YOUR_SCHOOL_NAME.mywconline.net/api.php?type=APPT&date=(yesterdays_date_YYYYMMDD)&key=(md5_hash)

Might return a response like this:

[
  {
    ...
  },
  {
    "First Name": "Jane",
    "Last Name": "Doe",
    "Email Address": "[email protected]",
    "Schedule Title": "A schedule title",
    "Staff or Resource": "Jane",
    "Date": "2021-10-19",
    "Start Time": "9:00am",
    "End Time": "10:00am",
    "Walk-In": "",
    "No-Show": "",
    "Placeholder": "YES",
    "Online": "",
    "Focus": ""
  },
  {
    ...
  }
]

Client report forms

A “yesterday’s client report forms” request to this URL:

https://YOUR_SCHOOL_NAME.mywconline.net/api.php?type=CRF&date=(yesterdays_date_YYYYMMDD)&key=(md5_hash)

Might return a response like this:

[
  {
    ...
  },
  {
    "First Name": "Bill",
    "Last Name": "Doe",
    "Email Address": "[email protected]",
    "Schedule Title": "A schedule title",
    "Staff or Resource": "Jane",
    "Date": "2021-10-19",
    "Start Time": "10:00am",
    "End Time": "11:00am",
    "Length": "45",
    "Off-Schedule?": ""
  },
  {
    ...
  }
]

Orphan apppointments

A “yesterday’s appointments without associated client report forms” request to this URL:

https://YOUR_SCHOOL_NAME.mywconline.net/api.php?type=ORPHAN&date=(yesterdays_date_YYYYMMDD)&key=(md5_hash)

Might return a response like this:

[
  {
    ...
  },
  {
    "First Name": "Jane",
    "Last Name": "Doe",
    "Email Address": "[email protected]",
    "Schedule Title": "A schedule title",
    "Staff or Resource": "Jane",
    "Date": "2021-10-19",
    "Start Time": "9:00am",
    "End Time": "10:00am",
    "Walk-In": "",
    "No-Show": "",
    "Placeholder": "YES",
    "Online": "",
    "Focus": ""
  },
  {
    ...
  }
]

Available appointments

A “today’s available appointments” request to this URL:

https://YOUR_SCHOOL_NAME.mywconline.net/api.php?type=CRF&date=(todays_date_YYYYMMDD)&key=(md5_hash)

Might return a response like this:

[
  {
    ...
  },
    {
    "Schedule Title": "A schedule title",
    "Date": "2021-10-20",
    "Start Time": "3:00pm",
    "Resource": "Jane"
  },
  {
    ...
  }
]

Code examples

Postman

Put this in the pre-request Script of the collection surrounding your requests, and be using an environment with variables set for api_key and ip_address (with room for time_stamp and md5_hash variables to get set).

const moment = require('moment');
let now = moment.utc(); // Credit to https://community.postman.com/t/what-can-i-use-to-determine-todays-utc-time-period-provided-today-is-2017-11-17-pacific/281/2
now = now.subtract(5, 'h'); // At the time of this writing, WCOnline's server was in the Eastern Standard (never-daylight) time zone, or UTC-5.
const timestamp = now.format('MMDDYYYY') + '?' + now.format('HH'); // See https://devhints.io/moment for Moment.js datetime formatting letters
postman.setEnvironmentVariable('time_stamp', timestamp);
const hashme = environment.api_key + timestamp + '!' + environment.ip_address; // PHP example from docs:  md5($ACCESSAPIKEY.''.date('mdY').'?'.date('H').'!'.ACCESSIPADDRESS); // See https://www.php.net/manual/en/datetime.format.php for what the letters mean
const md5hash = CryptoJS.MD5(hashme).toString(); // Credit to https://stackoverflow.com/a/29119263
postman.setEnvironmentVariable('md5_hash', md5hash);

GET request, to this URL, substituting for YOUR_SCHOOL_NAME, DATA_TYPE, & DATA_DATE_YYYYMMDD as appropriate:

https://YOUR_SCHOOL_NAME.mywconline.net/api.php?type=DATA_TYPE&date=DATA_DATE&key={{md5_hash}}

If you need to play w/ your script, you can easily test the values of the variables you’re setting with a GET request to a “Postman echo” URL like this (just make sure nothing you pass it is a secret):

https://postman-echo.com/get?param1={{time_stamp}}&param2=helloworld&param3={{md5_hash}}

Python

from datetime import datetime, timedelta
import hashlib
import requests

api_key = "YOUR_API_KEY" # Change this, of course, and in production don't put this here, and change your key on the WCOnline side after testing on your PC; your local machine and your cloud backups totally have the data you typed here.
ip_address = "YOUR_DEDICATED_API_CLIENT_PUBLIC_IP_ADDRESS" # Change this, of course, and in production don't put this here.

now = datetime.utcnow() - timedelta(hours=5) # At the time of this writing, WCOnline's server was in the Eastern Standard (never-daylight) time zone, or UTC-5.  # Credit to https://stackoverflow.com/a/13624191
time_stamp = now.strftime("%m%d%Y?%H") # Credit to https://www.programiz.com/python-programming/datetime/strftime.  # PHP example from docs:  md5($ACCESSAPIKEY.''.date('mdY').'?'.date('H').'!'.ACCESSIPADDRESS);  # See https://www.php.net/manual/en/datetime.format.php for what the letters mean.
md5_hash = hashlib.md5((api_key + time_stamp + '!' + ip_address).encode()).hexdigest() # Credit to https://www.geeksforgeeks.org/md5-hash-python/
school_name = "YOUR_SCHOOL_NAME" # Change this, of course
data_type = "DATA_TYPE" # Change this, of course
data_date = "20211021" # Change this, of course
get_url = "https://{}.mywconline.net/api.php?type={}&date={}&key={}".format(school_name, data_type, data_date, md5_hash)

response = requests.request("GET", get_url)

print(response.text)
--- ---