Adventures with MindBody Online

For some time, I’ve been working with the Hot N Cool Yoga Club, developing their website and assisting them technically.

To manage their scheduling, clients and so on, the club uses a service called Mindbody, which handles basically everything they do. In developing the website, I had wondered if Mindbody offered an API, so that I could hook certain aspects of their system into the club’s website. Doing so would allow the website to be automatically updated with new information as the club updated Mindbody.

Fortunately, Mindbody does indeed offer an API. It’s surprisingly robust, and offers a number of ways to access it. The API service can be accessed using SOAP, GET and POST, and even includes a pseudo-SQL service.

For a while now, I’ve been fiddling with the API, but haven’t gotten around to integrating it into the club’s website. The reason for this is that there are no existing libraries for PHP that I’m aware of, which will handle the API in a sane way.

As a result, I’ve recently been working on developing my own library for doing so. I had been developing one from scratch, which accesses the API via POST (because SOAP is unwieldy to bother with, if you don’t have to).


The Mindbody Online (hereafter referred to as MBO) API is fairly straightforward, and is surprisingly well-documented. It is separated into seven “services”, including the SQL service.

  1. Appointment Service
  2. Class Service
  3. Client Service
  4. Region Service
  5. Sale Service
  6. Staff Service
  7. Select Service (SQL)

Each one of these handles a specific aspect of the business, such as appointment scheduling, class scheduling and so on. Each one comes with a “formal service description”, which is a WSDL document describing the functions and structures in the API.

Initially, I wasn’t aware of the sheer usefulness of the WSDL format, so I chugged along for some time developing a POST-based library from scratch. I’ll probably merge my POST-based library with the WSDL-derived SOAP one, providing a common interface for the two, in order to provide multiple means of accessing the API.

Building the Library

In order to build what is to become my SOAP library for the MBO API, I solicited a bit of help from my favourite PHP community, #phpc on Freenode.

Apparently, the WSDL format is a rather complete description of the services, and can be loaded directly into the SOAP client. This provides the ability to access the API without having to write custom classes, as I had been doing for my POST-based library.

Additionally, there is a tool called wsdl2php, which can take a WSDL specification and turn it into a set of PHP classes, which annihilates all of the grunt work necessary to make the SoapClient functional. Normally, you would have to create all of the corresponding structures for the SoapClient manually, which is a major pain in the tuchus.


Once wsdl2php was installed, making sure that I had the zlib, SOAP and DOM extensions enabled in PHP, I could begin creating my service handlers.

Running wsdl2php with each WSDL document, a file was generated for each service. Individually, the resulting libraries worked fine. They could be used to query each of the services, but they could not be used together.

The reason for this is that each of the services’ WSDL documents defines the structures that it needs available, and some of the services share common structures. wsdl2php packs all of the structures into a single file, so when you go to include each of the service files, they try to re-define structure classes. This conflict makes it impossible to use multiple services without splitting each of the structures into its own file.

Splitting the Structures

Luckily, Matthew Turland (a.k.a. Elazar) has had some experience with exactly what I’m doing, where an API has been split into multiple services. He has written a tool for extracting each file into individual files for each structure.

After using his tool to extract all of the classes into individual files, I found that I had almost 200 files. Including them all manually before using them would be a mess, so I moved all of the service files (*_x0020_*.php) into a directory called “services”, and then moved the remaining files into a directory called “structures”.

Luckily, each service class has a map of all of the structures it uses, in a member variable called “classmap”. I simply patched the constructor for each service class with the following code:

foreach($options['classmap'] as $key => $value) if(file_exists("../structures/{$value}.php")) include_once("../structures/{$value}.php");

For the uninitiated, what this does is take each class specified in the class map, and the dynamically loads the corresponding file in the /structures/ directory. This is useful in that it eliminates the massive overhead that would be generated by loading all of the structures, instead only loading the structures when a service needs them.

The Result

So, now, we have all of the services and their structures generated automatically, with the code being fully documented via the WSDL.

An example usage of the Region Service would be as follows:


$parameters = new getRegions();
$parameters->Sourcename = "ASourceName";
$parameters->Password = "APassword";

$regionService = new Region_x0020_Service();
$result = $regionService->getRegions($parameters);
// $result now contains a getRegionsResult containing each region

This is still a bit clunky, as you have to create “parameters” objects corresponding to each function before you call them, but I intend handle all of that in another part of the library, which will provide “programmer-friendly” interfaces to this SoapClient.


I’m still in the process of building my library, but this should provide some insight into the process used to create it, should anyone have a similar project.

Once I’ve completed my library, I’ll update this post with more information.
For now, you can take a look at my SVN repository for this project.
Additionally, I’ve created a CLI tool for testing MBO queries.

January 17th, 2010: With the update of MBO API to 0.5, I’ve tagged the 0.4 version of the SOAP library which is relevant to this post. Once the 0.5 API is figured out, I’ll either update this post, or make a new one.

July 30th, 2011: I am moving this project to github, in order to take advantage of the benefits it has to offer. For now, I’ve moved the SOAP API library only, but I will see about moving the other subprojects as well. I will keep the current state of the domain-local subversion repository, but any future updates will take place in the github repository.

  1. #1 by Michael K on December 20, 2010 - 9:41 am

    Hi Justin,

    This is great. How is the progress going on the php classes for MBO API?

    • #2 by Justin Martin on December 20, 2010 - 12:14 pm

      I haven’t touched the classes since I wrote this blog post, but it’s part of an ongoing project I’m working on, which I’ll be continuing work on in the near future.

      You can always take a look at the Subversion repository linked at the bottom of the post, which will contain all of my work to-date on the classes.

  2. #3 by Jackson on December 28, 2010 - 7:42 am

    Awesome! I’m poking through the svn repo now. Thanks for sharing this. I wish you well in your endeavor. Hopefully I can glean something from this to make my project easier.

    Warm regards,

  3. #4 by roland on December 30, 2010 - 12:40 am

    great work! Have you ported this to the 0.5 API yet? I am trying to build a component for Joomla (which runs our site), so that I can display live data from MindBody. Unfortunately, I am getting a “System.NullReferenceException” error (getClasses is not an object) – I changed all references of 0_4 to 0_5 which seemed to work. Now – however – I am stuck.

    • #5 by Justin Martin on December 30, 2010 - 6:27 pm

      It’s been a while since I looked at the work, but if I recall correctly, the POST version of the library is unfinished. The only one that functions is the SOAP version, which is purely auto-generated.

      Take a look at librarygenerator.php, which is what is used to generate the library for any given version of the API. Change the $services array to reflect the new version of the API ( as an example). It’ll probably work right off the bat.

      • #6 by roland on January 7, 2011 - 1:58 am

        yeah, not quite “off the bat”. Although I can open the wsdl files in the browser, I cannot get wsdl2php to work … I get the “Failed to fetch …” error … blah!

    • #7 by Michael K on January 4, 2011 - 8:19 am

      Hi Roland,

      did you get your Joomla component working yet? We have a need for some working components on the 0.5 API also.

      Cheers, Michael

      • #8 by roland on January 10, 2011 - 12:18 am

        Michael, can you give me a short rundown on what you would expect from such a component, please? Mail it to me here office[at]

  4. #9 by roland on January 6, 2011 - 4:43 am

    Thanks Justin, I’ll check and try that!
    @Michael, not yet – busy working on it.

  5. #10 by roland on January 7, 2011 - 4:56 am

    Hi Justin,
    I am getting this error, when implementing your sample code:
    “Fatal error: Class ‘getRegions’ not found”
    This corresponds to the line 4 in your example. Checking the code, I understand that there is no getRegions class, only a getRegions function – can you please explain that to me?
    Warm regards

    • #11 by Justin Martin on January 7, 2011 - 7:52 am

      Hi Roland,

      getRegions is a method within the Region_x0020_Service class. It is not a class unto itself. Within the “services” directory of the SOAP client, there are classes corresponding to each service that the Mindbody API provides.

      Instantiate the service class, then one of the request structures (such as the getRegions class in the structures directory). Then, pass that request structure to the method of the service.

      It’s not pretty, but that’s how Mindbody implements it on their end.

      • #12 by roland on January 12, 2011 - 1:15 am

        ok, that worked. MB’s forum is really crap with the “spam detection” …. now I am getting a System.NullReferenceException when calling the GetClasses method … damn frustrating!

  6. #13 by Adam on March 20, 2011 - 6:22 am

    Has anyone actually gotten a full implementation to work with these services? getClients doesn’t seem to work. GetClassRequest will return all the classes, but using a filter of ClassIDs to return just one doesn’t return it (even though it was returned in the previous array). I emailed support and they said they couldn’t help with anything API related.

    I’m starting to think the API is a hoax that doesn’t even support a quarter of the functions it says it does.

  7. #15 by Adam on March 20, 2011 - 8:35 pm

    Well, right at this moment I’m just trying to use the ValidateLoginRequest and it keeps telling me the credentials are not valid. However, I am using the same credentials to log in through the regular site.

  8. #17 by Michael Rosner on April 28, 2011 - 10:26 pm

    Another note of thanks to you for posting this. It’s all been a real help.

    Wondering though if you ever got the 0.4 SelectService to work. I’ve been able to send queries successfully, and the number of rows returned seems to be correct, but the rows themselves have no content. Any idea what I might be missing?

    Thanks for any info you can provide.

    • #18 by Justin Martin on May 8, 2011 - 10:06 am

      I never really had the opportunity to make use of the SELECT service, so I don’t know a whole lot about how it’s used. It’s worth mentioning, some queries in the general API require an explicit filter for the types of data you want back.

      If you could post an example of your query, I’ll give it a look. You might also post a topic on the Mindbody GetSatisfaction forum, if you’ve not yet.

  9. #19 by Chris on May 11, 2011 - 9:50 am

    Thanks for writing this all up – it is very useful!

    The link for the CLI tool appears to be broken. Has it been moved to a new location? Thanks!

    • #20 by Justin Martin on May 12, 2011 - 11:26 am

      I don’t think I’ve updated the CLI tool yet for the 0.5 version of the API. I’ll take a look at that later today.

  10. #21 by RTM on July 19, 2011 - 4:34 pm

    My company is working with a Medical Spa running on Mindbody. Their problem is that Mindbody has no medically oriented treatment note templates (they only have contact logs with free text). If we wrote an app for the notes could it connect to Mindbody’s API? It would need to hang off the customer object somehow. Is this something you could help us with? Thanks.

    • #22 by Justin Martin on July 19, 2011 - 5:23 pm

      HI there. I’ve had a couple of enquiries about accessing contact logs through the API, and I had intended to send Mindbody’s API team an email about this matter. Once I get a response, I’ll let you know whether it’s possible.

  11. #23 by RTM on July 25, 2011 - 9:59 am

    Thanks Justin, let me know what they say. I’ll also need a developer to do the work so contact me if you’re interested.

    • #24 by Justin Martin on July 28, 2011 - 3:51 pm

      Hi. Sorry for the delay in responding. I received a reply back from Mindbody’s API team saying that while they do have plans to implement contact log access in the API in the future, they do not have a timeline.

      I’ll send you an email about the possible work available.

  12. #25 by Juan Sutton on July 28, 2011 - 9:34 pm

    Justin, do you think the contact log could be accessed via an SQL query?

    • #26 by Justin Martin on July 28, 2011 - 9:52 pm

      Hi Juan. To my knowledge, there is absolutely no existing way of accessing the contact log data through the API, or any means other than the website. Since it’s a fairly popular request, what I might try doing is writing a “screen scraper” utility to poll that data, but it would be entirely *one-way*. I’d have to find a willing client to be able to do that, though, as my existing client who uses Mindbody does not make use of contact logs.

  13. #27 by Ethan on August 3, 2011 - 11:41 am

    Thank you so much Justin Martin for this great library. It helped me so much with communicating with mind body api.

  14. #28 by Mike on August 30, 2011 - 10:18 am

    Anyone know of a good place to learn how to implement the APIs in a website? The Shop wants to do exactly what the Yoga Club wants to do, but I have no idea where to start placing the files, code, etc.



  15. #29 by Michael Rosner on October 12, 2011 - 7:38 pm

    Hi Justin,

    It’s been a few months and I wanted to let you know how far I’ve gone with the API. I am using version 0.5 now, and almost completely all with the DataService. Once I started using the CSV versions of the methods, everything fell into place. I’ve been able to get data from numerous tables that are not supported by the rest of the API, and I’m using queries to provide content for much of my client’s website.

    If I hadn’t found your posting, I doubt I would have been able to do all this. Thanks again!


    • #30 by Justin Martin on October 12, 2011 - 7:59 pm

      I’m very pleased to hear that my work has been of use to you. :)

      If you do have any code to contribute back, or perhaps just an explanation of the best way to manage the data, I’m sure many would appreciate it. Let me know, and I’ll link to it here, so it’s easily found.

      • #31 by Michael Rosner on March 5, 2012 - 12:45 am

        Sorry I haven’t checked in for a while. I see you updated your Mindbody library recently. I thought I’d mention that there are two more services that you haven’t included, the FinderService and the DataService.

        I’ve got some sample code that uses the DataService that I’d like to send to you. What’s the best way to do that? (It will also provide an answer to Juan’s inquiry about the available tables in MindBody.)

        • #32 by Justin Martin on March 22, 2012 - 7:48 am

          Hi Michael. Thanks for the heads-up. I’ve updated the library to include the Data and Finder services.

          • #33 by ss on April 25, 2012 - 1:59 am

            Were you able to book a class using FinderCheckoutShoppingCart method in Finder Service?
            Is MBFClassID same as MINDBODYID column of tblAggregateClasses table?

    • #34 by Juan Sutton on January 9, 2012 - 11:32 pm

      Hi Michael, would you share the available table names that you are aware of?

      • #35 by Michael Rosner on March 26, 2012 - 2:50 pm

        Juan, using the SelectDataCSV method (I never could get the SelectDataXML to work) of the Data Service (which Justin added a few days ago, thanks Justin!) and the following query, you can get a list of all the tables available:

        “select * from sys.tables order by tablename”

        There are a few tables that I found which you can’t select data from: tblCCNumbers, tblCCTrans and tblEKey. But yes, there is a tblContactLogs table!

        Hope this is helpful.


    • #39 by bill patton on May 1, 2012 - 9:54 am


      i’m hoping to do similar actions with the MBO api – is there a place I could find your sample code and/or are you available for hire to create a basic collection of php pages that call MBO data?

      -bill patton

  16. #40 by Carlen on November 20, 2011 - 1:08 am

    Hi Justin,

    First, thank you for posting your work. People like you make the development community so welcoming.

    I am new to websites, feel comfortable in html, css, and learning php, but for some reason cannot setup a soapclient. I was wondering if you had a simple explanation for the processes of installing a SOAP extension for this project. Perhaps with a stored set of SourceCredentials.


    • #41 by Justin Martin on November 20, 2011 - 8:35 am

      Hi Carlen,

      It would be difficult to explain how to set up and use SOAP, here, so I suggest you connect to ##php on Freenode ( That channel can assist you in setting up SOAP, and may even be able to give you some guidance on using it.

      If you do have specific questions about the MBO library I’ve written, I’m available in #phpc on Freenode.

  17. #42 by Kevin Penner on February 24, 2012 - 9:10 am

    What portion of the Hot N Cool Yoga Club website is using this API? I can not see any kind of linking of data when I look at this site.


  18. #43 by Tom on March 22, 2012 - 6:24 am

    I used your sample request listed in the documentation off of git but was promptly greeted with Fatal error: Call to undefined function class_alias(). I am unable to install WSDL2PHP because i am on shared hosting. Any suggestions?

    • #44 by Justin Martin on March 22, 2012 - 6:55 am

      Hi Tom. PHP 5.2 is past its end of life. The class_alias function became available in PHP 5.3.0, so you’ll need to be using that to use my library. If you’re particularly desperate, you can install PHP and WSDL2PHP locally, edit the librarygenerator.php script to remove the class alias function call, and then run it. This will produce a new copy of the library.

  19. #45 by Gaius on April 2, 2012 - 2:07 pm


    I am looking for a web developer to create a custom reporting tool for mindbody. My employer wants to be able to get certain informaion which is not available in the standard reports baked into Mindbody.

    Our goal Is to create a tool which will give us the access to a broader set of data than what we can get now using the standard reports.

    Please send me an email and let me know if this is something you would be capable and interested in helping us with.

    Thank you

    • #46 by James r on January 15, 2013 - 1:51 am

      Hi I have a site setup that does just this lease let me know if you are still interested


  20. #47 by Jeffrey Wood on April 24, 2012 - 8:40 pm

    Hi Justin, Very interested in getting MBO integrated into our company website, Appointment Scheduler as a priority. Please contact me if you’d be interested in some dev work. Thanks.

  21. #48 by Matt Habermehl on May 10, 2012 - 5:57 am

    Brilliant. This saved me an enormous amount of time. Thank you SO much for offering this to the community.

    Here’s something that I didn’t notice explicitly mentioned in the documentation, but may be of help to some users:

    When querying classes, mindbody will return only today’s classes by default unless you provide a start and end date. It may not be immediately obvious how to do this. This is what I did (not sure if the formatting will stick once comment is posted):

    try {
    $enddate = new DateTime('2013-01-01');
    } catch (Exception $e) {
    echo $e->getMessage();

    $startdateformat = $startdate->format('Y-m-d');
    $enddateformat = $enddate->format('Y-m-d');

    $service = new Class_Service();

    $parameters = new GetClasses();
    $parameters->Request = new GetClassesRequest();

    $parameters->Request->SourceCredentials = new SourceCredentials();
    $parameters->Request->SourceCredentials->SourceName = "SourceName";
    $parameters->Request->SourceCredentials->Password = "Password";
    $parameters->Request->SourceCredentials->SiteIDs = array(
    numerical site id
    // This is how to set parameters for the request that are available via the API:
    $parameters->Request->StartDateTime = $startdateformat;
    $parameters->Request->EndDateTime = $enddateformat;

    $classes = $service->GetClasses($parameters);



    • #49 by Bruno on June 17, 2012 - 12:47 pm

      Hi Matt,
      Could you please share all of the code?
      I’m trying to implement it now.


      • #50 by Matt Habermehl on June 18, 2012 - 9:31 am

        I tried to post the full code twice, but the WordPress software stripped out the first part of it each time. In case it was the open PHP command that did it, I’ll just leave that out. Here’s hoping this works.
        Above, take out the first line “getMessage();” and replace it with the following (assuming it doesn’t strip it out again):


        //Date stuff
        try {
        $startdate = new DateTime(‘2012-05-01’);
        } catch (Exception $e) {
        echo $e->getMessage();

        then continue with what’s above, beginning with “exit(1);”

      • #51 by Matt Habermehl on June 18, 2012 - 9:35 am

        Some notes: The require(“services/Class_Service.php”) refers to a file in Justin’s library. So adjust the directory structure according to where you put the library.

        And you’re probably aware, but just in case: The source credentials are those that you have to explicitly request from MBO so as to access their API. This is not the same as the user’s MBO login info.

        • #52 by Bruno on June 19, 2012 - 4:32 am

          Hi Matt, I got the code to work but it spits out non-formatted stuff i.e. array(20) { [0]=> object(ClassSchedule)#10 (21) { [“Classes”]=> NULL [“Clients”]=> NULL [“Course”]=> NULL

          How would I go about formatting the output? Thanks!

          • #53 by Matt Habermehl on June 19, 2012 - 7:41 am

            That unformatted output is important. It gives you the structure of the $classes variable. When you’re looking at that output in your browser, make sure you view the page source to see it in a much more readable format. The structure is a bit complicated, but once you get a handle on how it’s organized, it makes dealing with data very easy.
            The $classes variable holds an object called GetClassesResponse. That object itself has a number of variables. To access them, you could do something like this:
            $classes = $classes->GetClassesResult;
            Now $classes holds the object GetClassesResult, which again in turn has a number of variables, one of which is an object called “Classes”. To access this object, do something like this:
            $classes = $classes->Classes
            One of the variabels in Classes is an array called “Class”. Access it:
            $classes = $classes->Class
            Each element of the array contains an object that has information about classes. You can, for example, print a list of class names:
            foreach($classes as $listing){
            echo $listing->ClassDescription->Name . ”;

            You’ll likely have to play around with it a bit, but I hope this gives you enough to figure the rest out. Cheers.

          • #54 by Bruno on June 19, 2012 - 9:01 am

            Thanks. I’ll work with it and see what I can do.

          • #55 by Matt Habermehl on June 19, 2012 - 7:47 am

            wordpress stripped out some HTML again. The statement inside the foreach loop was just meant to give you the class name, and then print an HTML break element so that each class would be listed on a new line.

  22. #56 by Tim M on June 18, 2012 - 5:51 pm

    I am looking for a developer that understands the Mindbody API and can integrate it into a mobile app. Please send me an email if you’re interested in some development work.

  23. #57 by John H on June 19, 2012 - 9:18 am

    Hey Tim ~
    What are you looking to do? I am also looking for some assistance in getting the MindBody API working in a mobile app. Specifically, I would like to be able to call the API using Javascript and then write out the class schedule to a page in the app.

    Has anyone attempted this and willing to share some code to get me started?


    • #58 by lac on August 16, 2012 - 3:24 pm

      Did you find what you are looking for ?

  24. #59 by Kyle on July 2, 2012 - 8:50 am

    Fatal error: Class ‘GetClasses’ not found in…on line 21.

    here is my code:


    $startdate = strtotime(‘2012-05-01’);

    $enddate = strtotime(‘2012-07-31’);

    $service = new Class_Service();

    line 21: $parameters = new GetClasses();
    $parameters->Request = new GetClassesRequest();

    $parameters->Request->SourceCredentials = new SourceCredentials();
    $parameters->Request->SourceCredentials->SourceName = “user”;
    $parameters->Request->SourceCredentials->Password = “pass”;
    $parameters->Request->SourceCredentials->SiteIDs = array(
    // This is how to set parameters for the request that are available via the API:
    $parameters->Request->StartDateTime = $startdate;
    $parameters->Request->EndDateTime = $enddate;

    $classes = $service->GetClasses($parameters);


    What exactly am I doing wrong to get this error? I am using Matt Habermehl’s example from above, and he did not seem to be receiving any error messages.

    • #60 by Savion S on August 13, 2012 - 7:59 am

      Kyle :
      Fatal error: Class ‘GetClasses’ not found in…on line 21.
      here is my code:
      $startdate = strtotime(’2012-05-01′);
      $enddate = strtotime(’2012-07-31′);
      $service = new Class_Service();
      line 21: $parameters = new GetClasses();
      $parameters->Request = new GetClassesRequest();
      $parameters->Request->SourceCredentials = new SourceCredentials();
      $parameters->Request->SourceCredentials->SourceName = “user”;
      $parameters->Request->SourceCredentials->Password = “pass”;
      $parameters->Request->SourceCredentials->SiteIDs = array(
      // This is how to set parameters for the request that are available via the API:
      $parameters->Request->StartDateTime = $startdate;
      $parameters->Request->EndDateTime = $enddate;
      $classes = $service->GetClasses($parameters);
      What exactly am I doing wrong to get this error? I am using Matt Habermehl’s example from above, and he did not seem to be receiving any error messages.

      I’m getting the same error…

    • #61 by eric on September 13, 2012 - 10:38 am

      Hi, I’m getting the same error. Do you solve this out?
      PHP Fatal error: Class ‘GetClasses’ not found in…

  25. #62 by Greg on September 17, 2012 - 3:51 am

    I’ve worked with the _04 version of the API using .NET. Now I want to develop a web app using PHP and the _05 version of the API. I’m a newbie to PHP and I haven’t figured out how to pass a SQL SELECT statement as a parameter to Data Service. How do I do this?

    Is it better to use the CSV call or the XML call? I am not sure how to process the response in either case. If someone has some code they are willing to share I’d would be very grateful.

    • #63 by Justin Martin on September 17, 2012 - 7:04 am

      Hi Greg,

      The Data service (aka select service) is primarily used when the other services do not provide the means to access certain information. It is not advised to use the data service at all, unless you absolutely have to.

  26. #64 by Andrea on April 15, 2014 - 8:51 am

    Did anyone ever get this to work? It has been two years since the last post and Mindbody still doesnt integrate with Squarespace very well.

(will not be published)