Implementing Whipcount Pages

Introduction

This is a short description of how we used the REST API to build the Whipcount CMS forms and pages.

Whipcount pages use the same action processing as the rest of ActionKit. However the code that publishes the Whipcount pages, the ActionKit "CMS", uses the REST API to access targets and results.

Remember that Whipcount pages let you target groups of key legislators and decision makers. Unlike Call pages, Whipcount pages allow non-constituents to make and report phone calls to legislative targets.

The Whipcount pages load information about targets for callers, as well as the results of previous calls. This document describes how we used the RESTful API to load the details of the page, the list of targets, created a form for reporting calls, and the results of calls made by users.

Loading A Whipcount Page

The RESTful API allows loading pages and their (many!) associated objects. We previously created a Whipcount page using the admin. Our page asks users to whip up votes for preschool education and targets the US House's Committee on Education and the Workforce.

To find our page via the API we can search for it using the name field:

GET https://www-default.actionkit.com:8807/rest/v1/whipcountpage/?name=preschoolsaveslives

{   'meta': {'limit': 20,
             'next': None,
             'offset': 0,
             'previous': None,
             'total_count': 1},
    'objects': [ { 'actions': /rest/v1/whipcountaction/?page=12,
                   'cms_form': /rest/v1/whipcountform/1/,
                   'created_at': '2012-10-22T13:01:12',
                   'fields': {   },
                   'followup': {   'email_body': u'',
                                   'email_custom_from': u'',
                                   'email_from_line': /rest/v1/fromline/1/,
                                   'email_subject': u'',
                                   'email_wrapper': /rest/v1/emailwrapper/1/,
                                   'id': 11,
                                   'notifications': [],
                                   'page': /rest/v1/whipcountpage/12/,
                                   'resource_uri': /rest/v1/pagefollowup/11/,
                                   'send_email': False,
                                   'send_notifications': False,
                                   'send_taf': False,
                                   'taf_body': u'',
                                   'taf_subject': u'',
                                   'url': /cms/thanks/preschoolsaveslives},
                   'goal': None,
                   'goal_type': u'actions',
                   'hidden': False,
                   'hosted_with': /rest/v1/hostingplatform/1/,
                   'id': 12,
                   'language': /rest/v1/language/100/,
                   'list': /rest/v1/list/1/,
                   'multilingual_campaign': None,
                   'name': u'preschoolsaveslives',
                   'required_fields': [],
                   'resource_uri': /rest/v1/whipcountpage/12/,
                   'status': u'active',
                   'tags': [],
                   'target_groups': [   {   'all': /rest/v1/targetgroup/4/all/,
                                            'created_at': '2012-10-22T13:00:54',
                                            'hidden': False,
                                            'id': 4,
                                            'name': u'U.S. House Committee on Education',
                                            'readonly': False,
                                            'resource_uri': /rest/v1/targetgroup/4/,
                                            'type': u'house',
                                            'updated_at': '2012-10-22T13:00:54'}],
                   'title': u'Support Universal Preschool!',
                   'type': u'Whipcount',
                   'updated_at': '2012-10-22T13:01:12',
                   'url': u'',
                   'whipcountform': {   'client_hosted': False,
                                        'client_url': u'',
                                        'created_at': '2012-10-22T13:02:48',
                                        'id': 1,
                                        'introduction_text': u'<p>Make some calls to whip up vote for early education!</p>',
                                        'minimum_calls': 3,
                                        'minimum_response_agreement': u'0.66',
                                        'page': /rest/v1/whipcountpage/12/,
                                        'resource_uri': /rest/v1/whipcountform/1/,
                                        'results_source': u'users',
                                        'script_text': u"<p>Hi,</p>\r\n<p>I'm calling to ask you to support Bill HR. XYZ01 - which increases federal funding for pre-K education. Preschool saves lives!</p>\r\n<p>Can I count on your support?</p>\r\n<p>(Record answer)</p>\r\n<p>Thank you for your time,</p>\r\n<p>[Your name]</p>",
                                        'survey_question_text': u"<p>How'd it go?</p>",
                                        'templateset': /rest/v1/templateset/1/,
                                        'thank_you_text': u'<p>Thanks for making a call!</p>',
                                        'updated_at': '2012-10-22T13:03:58'}}]}

The results include the page we just created, with the resource uri /rest/v1/whipcountpage/12/. Loading that URI returns the same details that are included in the search results.

Let's look at a few of those fields in more detail:

  • title - Page title.
  • name - A unique identifier for the page, has to be included in submitted forms as the parameter page.
  • whipcountform - The content we wrote in the admin for our page's introduction, script, and thank you text.
  • actions - A link to all the actions take on this page
  • required - A list of required fields. Email or akid is always required, even if this list is empty.
  • target_groups - A list of TargetGroups.

Now, most of that is pretty easy to understand, it's just text we can display in a page! But how do we show users a list of the legislators we're targeting? The returned data includes a list of Target Groups, but we want a list of Targets.

Loading A list Of Targets

Targets are organized into TargetGroups, and normally we find a single Target for a constituent. However, for whipcount pages, we want to find all the Targets ignoring constituencies.

In the returned data for the page (see previous section), the target group for the Committee on Education and the Workforce looks like this:

{ 'all': /rest/v1/targetgroup/4/all/,
  'created_at': '2012-10-22T13:00:54',
  'hidden': False,
  'id': 4,
  'name': u'U.S. House Committee on Education',
  'readonly': False,
  'resource_uri': /rest/v1/targetgroup/4/,
  'type': u'house',
  'updated_at': '2012-10-22T13:00:54' }

The first item "all" is a link to the full list of Targets. How handy! The full list is is a bit long, but here are the first few targets included in the returned response:

GET https://www-default.actionkit.com:8807/rest/v1/targetgroup/4/all/

{  'meta': {'limit': 20,
            'next': /rest/v1/targetgroup/4/all/?_offset=20&_limit=20,
            'offset': 0,
            'previous': None,
            'total_count': 40},
    'objects': [  {'country': u'United States',
                   'created_at': '2012-10-22T08:42:26',
                   'email': u'http://roby.house.gov/Contact/emailmartha.htm',
                   'fax': u'(202) 225-8913',
                   'first': u'Martha',
                   'full_name': u'Martha Roby',
                   'gender': u'F',
                   'hidden': False,
                   'id': 7659,
                   'last': u'Roby',
                   'long_title': u'Representative',
                   'party': u'R',
                   'phone': u'(202) 225-2901',
                   'resource_uri': /rest/v1/target/7659/,
                   'seat': u'',
                   'state': u'AL',
                   'their': u'her',
                   'theirs': u'hers',
                   'them': u'her',
                   'they': u'she',
                   'title': u'Rep.',
                   'title_full': u'Representative Martha Roby',
                   'title_last': u'Rep. Roby',
                   'type': u'house',
                   'updated_at': '2012-10-22T08:42:26',
                   'us_district': u'AL_02'},
               {   'country': u'United States',
                   'created_at': '2012-10-22T08:42:26',
                   'email': u'http://aderholt.house.gov/email-me2/',
                   'fax': u'(202) 225-5587',
                   'first': u'Robert',
                   'full_name': u'Robert Aderholt',
                   'gender': u'M',
                   'hidden': False,
                   'id': 7682,
                   'last': u'Aderholt',
                   'long_title': u'Representative',
                   'party': u'R',
                   'phone': u'(202) 225-4876',
                   'resource_uri': /rest/v1/target/7682/,
                   'seat': u'',
                   'state': u'AL',
                   'their': u'his',
                   'theirs': u'his',
                   'them': u'him',
                   'they': u'he',
                   'title': u'Rep.',
                   'title_full': u'Representative Robert Aderholt',
                   'title_last': u'Rep. Aderholt',
                   'type': u'house',
                   'updated_at': '2012-10-22T08:42:26',
                   'us_district': u'AL_04'},
               {   'country': u'United States',
                   'created_at': '2012-10-22T08:42:26',
                   'email': u'http://grijalva.house.gov/index.cfm?sectionid=126&sectiontree=2,126',
                   'fax': u'(202) 225-1541',
                   'first': u'Raul',
                   'full_name': u'Raul Grijalva',
                   'gender': u'M',
                   'hidden': False,
                   'id': 7764,
                   'last': u'Grijalva',
                   'long_title': u'Representative',
                   'party': u'D',
                   'phone': u'(202) 225-2435',
                   'resource_uri': /rest/v1/target/7764/,
                   'seat': u'',
                   'state': u'AZ',
                   'their': u'his',
                   'theirs': u'his',
                   'them': u'him',
                   'they': u'he',
                   'title': u'Rep.',
                   'title_full': u'Representative Raul Grijalva',
                   'title_last': u'Rep. Grijalva',
                   'type': u'house',
                   'updated_at': '2012-10-22T08:42:26',
                   'us_district': u'AZ_07'},
               {   'country': u'United States',
                   'created_at': '2012-10-22T08:42:26',
                   'email': u'https://woolseyforms.house.gov/index.cfm?sectionid=80',
                   'fax': u'(202) 225-5163',
                   'first': u'Lynn',
                   'full_name': u'Lynn Woolsey',
                   'gender': u'F',
                   'hidden': False,
                   'id': 7806,
                   'last': u'Woolsey',
                   'long_title': u'Representative',
                   'party': u'D',
                   'phone': u'(202) 225-5161',
                   'resource_uri': /rest/v1/target/7806/,
                   'seat': u'',
                   'state': u'CA',
                   'their': u'her',
                   'theirs': u'hers',
                   'them': u'her',
                   'they': u'she',
                   'title': u'Rep.',
                   'title_full': u'Representative Lynn Woolsey',
                   'title_last': u'Rep. Woolsey',
                   'type': u'house',
                   'updated_at': '2012-10-22T08:42:26',
                   'us_district': u'CA_06'}
        ]}

We use this list to show our users the full list of targets. For performance reasons, you probably don't want to use this technique for Whipcount pages that have large target groups - e.g. the U.S. House.

Creating A Form For A Target

It's fine and dandy to show a list of Targets, but we really want to let users report the results of their calls and display those results.

Whipcount pages rely on all the same fields as other actions. But they use a special syntax for associating the results of the calls with targets.

Here's the part of our form with the elements relevant for saving WhipCount results to the database:

<div id="who_to_call">
<div class="target">
<label class="step" for="7659">
Step 1. Call Representative Martha Roby at <b class="phone">(202) 225-2901</b>
</label>
</div>
</div>

<div id="what_to_say"> <div>Call and say:</div> <p>Hi,</p> <p>I'm calling to ask you to support Bill HR. XYZ01 - which increases federal funding for pre-K education. Preschool saves lives!</p> <p>Can I count on your support?</p> <p>(Record answer)</p> <p>Thank you for your time,</p> <p>[Your name]</p> </div>

<div id="response">
<label class="step has-content" style="cursor: text; pointer-events: none;">Step 2. Report back</label>
<div><label for="response-7659">What is Representative Martha Roby's stance on this issue?</label></div>
<ul>
<li><label for="uncommitted-7659"><input type="radio" name="response-7659" value="uncommitted" id="uncommitted-7659">Uncommitted/Did not answer</label></li>
<li><label for="supportive-7659"><input type="radio" name="response-7659" value="supportive" id="supportive-7659">Supportive of our position</label></li>
<li><label for="opposed-7659"><input type="radio" name="response-7659" value="opposed" id="opposed-7659">Opposed to our position</label></li>
<li><label for="message-7659"><input type="radio" name="response-7659" value="message" id="message-7659">Left a message</label></li>
</ul>
<input type="hidden" name="required" value="response-7659">
<input type="hidden" name="error_response-7659:missing" value="Please let us know what Rep. Roby responded!">
</div>

Each target should have an input named "response-XXXX" where XXX is the id field in the details for that Target. The action processing will save the value of the response as WhipcountActionCalled objects, stored in the table core_whipcountactioncalled.

The action processing doesn't care what the response is, but ActionKit's built in Whipcount CMS pages look for one of the following responses:

  • uncommitted
  • supportive
  • opposed
  • message

One response per target per caller is recorded. Later calls will over-write earlier calls.

The same format could be sent to the API action processing in JSON format.

{ 'response-XXXX': 'responsetext', ... }

where XXXX is the target's id field.

Fetching The Results Of The Calls

Whipcount action processing saves reported responses as WhipcountActions and associated WhipcountActionCalled objects. A WhipcountActionCalled instance associates a WhipcountAction, a Target and a response. The response can be anythign, but we used "uncommitted", "supportive", "opposed" or "message".

We could use the list endpoint for WhipcountActionCalled to find all the responses and compile totals.

GET https://www-default.actionkit.com:8807/rest/v1/whipcountactioncalled/?whipcountaction__page__name=preschoolsaveslives
{
"meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 7
},
"objects": [
    {
        "created_at": "2012-10-23T16:21:17",
        "id": 1,
        "resource_uri": "/rest/v1/whipcountactioncalled/1/",
        "response": "opposed",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:26",
            "email": "http://roby.house.gov/Contact/emailmartha.htm",
            "fax": "(202) 225-8913",
            "first": "Martha",
            "full_name": "Martha Roby",
            "gender": "F",
            "hidden": false,
            "id": 7659,
            "last": "Roby",
            "long_title": "Representative",
            "party": "R",
            "phone": "(202) 225-2901",
            "resource_uri": "/rest/v1/target/7659/",
            "seat": "",
            "state": "AL",
            "their": "her",
            "theirs": "hers",
            "them": "her",
            "they": "she",
            "title": "Rep.",
            "title_full": "Representative Martha Roby",
            "title_last": "Rep. Roby",
            "type": "house",
            "updated_at": "2012-10-22T08:42:26",
            "us_district": "AL_02"
        },
        "updated_at": "2012-10-23T16:21:17",
        "whipcountaction": "/rest/v1/whipcountaction/3/"
    },
    {
        "created_at": "2012-10-23T16:21:57",
        "id": 2,
        "resource_uri": "/rest/v1/whipcountactioncalled/2/",
        "response": "supportive",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:26",
            "email": "http://aderholt.house.gov/email-me2/",
            "fax": "(202) 225-5587",
            "first": "Robert",
            "full_name": "Robert Aderholt",
            "gender": "M",
            "hidden": false,
            "id": 7682,
            "last": "Aderholt",
            "long_title": "Representative",
            "party": "R",
            "phone": "(202) 225-4876",
            "resource_uri": "/rest/v1/target/7682/",
            "seat": "",
            "state": "AL",
            "their": "his",
            "theirs": "his",
            "them": "him",
            "they": "he",
            "title": "Rep.",
            "title_full": "Representative Robert Aderholt",
            "title_last": "Rep. Aderholt",
            "type": "house",
            "updated_at": "2012-10-22T08:42:26",
            "us_district": "AL_04"
        },
        "updated_at": "2012-10-23T16:21:57",
        "whipcountaction": "/rest/v1/whipcountaction/3/"
    },
    {
        "created_at": "2012-10-23T16:22:28",
        "id": 3,
        "resource_uri": "/rest/v1/whipcountactioncalled/3/",
        "response": "uncommitted",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:27",
            "email": "https://susandavisforms.house.gov/forms/writeyourrep/",
            "fax": "(202) 225-2948",
            "first": "Susan",
            "full_name": "Susan Davis",
            "gender": "F",
            "hidden": false,
            "id": 8060,
            "last": "Davis",
            "long_title": "Representative",
            "party": "D",
            "phone": "(202) 225-2040",
            "resource_uri": "/rest/v1/target/8060/",
            "seat": "",
            "state": "CA",
            "their": "her",
            "theirs": "hers",
            "them": "her",
            "they": "she",
            "title": "Rep.",
            "title_full": "Representative Susan Davis",
            "title_last": "Rep. Davis",
            "type": "house",
            "updated_at": "2012-10-22T08:42:27",
            "us_district": "CA_53"
        },
        "updated_at": "2012-10-23T16:22:28",
        "whipcountaction": "/rest/v1/whipcountaction/3/"
    },
    {
        "created_at": "2012-10-26T12:37:58",
        "id": 4,
        "resource_uri": "/rest/v1/whipcountactioncalled/4/",
        "response": "supportive",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:27",
            "email": "https://susandavisforms.house.gov/forms/writeyourrep/",
            "fax": "(202) 225-2948",
            "first": "Susan",
            "full_name": "Susan Davis",
            "gender": "F",
            "hidden": false,
            "id": 8060,
            "last": "Davis",
            "long_title": "Representative",
            "party": "D",
            "phone": "(202) 225-2040",
            "resource_uri": "/rest/v1/target/8060/",
            "seat": "",
            "state": "CA",
            "their": "her",
            "theirs": "hers",
            "them": "her",
            "they": "she",
            "title": "Rep.",
            "title_full": "Representative Susan Davis",
            "title_last": "Rep. Davis",
            "type": "house",
            "updated_at": "2012-10-22T08:42:27",
            "us_district": "CA_53"
        },
        "updated_at": "2012-10-26T12:37:58",
        "whipcountaction": "/rest/v1/whipcountaction/4/"
    },
    {
        "created_at": "2012-10-26T12:38:30",
        "id": 5,
        "resource_uri": "/rest/v1/whipcountactioncalled/5/",
        "response": "uncommitted",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:27",
            "email": "https://biggertforms.house.gov/index.cfm?sectionid=81",
            "fax": "(202) 225-9420",
            "first": "Judy",
            "full_name": "Judy Biggert",
            "gender": "F",
            "hidden": false,
            "id": 7944,
            "last": "Biggert",
            "long_title": "Representative",
            "party": "R",
            "phone": "(202) 225-3515",
            "resource_uri": "/rest/v1/target/7944/",
            "seat": "",
            "state": "IL",
            "their": "her",
            "theirs": "hers",
            "them": "her",
            "they": "she",
            "title": "Rep.",
            "title_full": "Representative Judy Biggert",
            "title_last": "Rep. Biggert",
            "type": "house",
            "updated_at": "2012-10-22T08:42:27",
            "us_district": "IL_13"
        },
        "updated_at": "2012-10-26T12:38:30",
        "whipcountaction": "/rest/v1/whipcountaction/4/"
    },
    {
        "created_at": "2012-10-26T12:38:55",
        "id": 6,
        "resource_uri": "/rest/v1/whipcountactioncalled/6/",
        "response": "supportive",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:27",
            "email": "https://forms.house.gov/altmire/webforms/issue_subscribe.htm",
            "fax": "(202) 226-2274",
            "first": "Jason",
            "full_name": "Jason Altmire",
            "gender": "M",
            "hidden": false,
            "id": 7878,
            "last": "Altmire",
            "long_title": "Representative",
            "party": "D",
            "phone": "(202) 225-2565",
            "resource_uri": "/rest/v1/target/7878/",
            "seat": "",
            "state": "PA",
            "their": "his",
            "theirs": "his",
            "them": "him",
            "they": "he",
            "title": "Rep.",
            "title_full": "Representative Jason Altmire",
            "title_last": "Rep. Altmire",
            "type": "house",
            "updated_at": "2012-10-22T08:42:27",
            "us_district": "PA_04"
        },
        "updated_at": "2012-10-26T12:38:55",
        "whipcountaction": "/rest/v1/whipcountaction/4/"
    },
    {
        "created_at": "2012-10-26T12:39:29",
        "id": 7,
        "resource_uri": "/rest/v1/whipcountactioncalled/7/",
        "response": "opposed",
        "target": {
            "country": "United States",
            "created_at": "2012-10-22T08:42:26",
            "email": "https://writerep.house.gov/writerep/welcome.shtml",
            "fax": "(202) 225-8354",
            "first": "Bobby",
            "full_name": "Bobby Scott",
            "gender": "M",
            "hidden": false,
            "id": 7725,
            "last": "Scott",
            "long_title": "Representative",
            "party": "D",
            "phone": "(202) 225-8351",
            "resource_uri": "/rest/v1/target/7725/",
            "seat": "",
            "state": "VA",
            "their": "his",
            "theirs": "his",
            "them": "him",
            "they": "he",
            "title": "Rep.",
            "title_full": "Representative Bobby Scott",
            "title_last": "Rep. Scott",
            "type": "house",
            "updated_at": "2012-10-22T08:42:26",
            "us_district": "VA_03"
        },
        "updated_at": "2012-10-26T12:39:29",
        "whipcountaction": "/rest/v1/whipcountaction/4/"
    }
  ]
}

But that's going to get really slow when we have a lot of responses! Not a very scalable solution. SQL is a much better tool for getting the results.

The RESTful API provides a mechanism for running ad-hoc SQL. We'll use it to get the totals for our Whipcount page.

Note

I'm just going to magically write the SQL that we'll use, but obviously figuring out this query is non-trivial. The Advanced data structure and SQL tips section of the manual has helpful details on tables - http://docs.actionkit.com/docs/manual/developer/wmd.html

Our reporting query groups actions on our page by target, counts the different responses and filters by some minimums to protect against a few people gaming the system. The query uses several parameters - using the same syntax as reports, e.g. {{ param }} - which we'll provide when we run it.

SELECT responses.target_id,
   SUM(IF(responses.response='supportive',1,0)) supportive,
   SUM(IF(responses.response='opposed',1,0)) opposed,
   SUM(IF(responses.response='uncommitted',1,0)) uncommitted,
   COUNT(responses.whipcountaction_id) calls
 FROM core_action action
 JOIN core_page page ON (action.page_id=page.id)
 JOIN core_whipcountaction calls ON (action.id=calls.action_ptr_id)
 JOIN core_whipcountactioncalled responses ON (calls.action_ptr_id=responses.whipcountaction_id)
WHERE
    page.name = {{ page }}
GROUP BY 1
HAVING
    (supportive/calls >= {{ minimum_response_agreement }}
      OR opposed/calls >= {{ minimum_response_agreement }}
      OR uncommitted/calls >= {{ minimum_response_agreement }})
     AND calls >= {{ minimum_calls }}

There's a lot going on in that query, but we're going to focus on how to run it. The RESTful API has several ways to run a query - as a synchronous or asynchronous report, or as an ad-hoc query.

To keep things simple, we're going to use the ad-hoc method , but you can read more about running reports via the API.

To run an ad-hoc query we make a request to /rest/v1//report/run/sql/ with a query parameter. Let's try a simple example.

GET https://www-default.actionkit.com:8807/rest/v1/report/run/sql/?query=select+now()
[["2012-10-26 13:52:27"]]

Easy! Now let's plug our our complicated query in. (Note that you can set parameters in your queries the same way you can in reports - this is actually creating a temporary report.)

GET https://www-default.actionkit.com:8807/rest/v1/report/run/sql/?minimum_response_agreement=0&format=json&minimum_calls=0&page=preschoolsaveslives&query=%0A++++select+responses.target_id%2C%0A+++++++sum%28if%28responses.response%3D%27supportive%27%2C1%2C0%29%29+supportive%2C%0A+++++++sum%28if%28responses.response%3D%27opposed%27%2C1%2C0%29%29+opposed%2C%0A+++++++sum%28if%28responses.response%3D%27uncommitted%27%2C1%2C0%29%29+uncommitted%2C%0A+++++++count%28responses.whipcountaction_id%29+calls%0A+++++from+core_action+action%0A+++++join+core_page+page+on+%28action.page_id%3Dpage.id%29%0A+++++join+core_whipcountaction+calls+on+%28action.id%3Dcalls.action_ptr_id%29%0A+++++join+core_whipcountactioncalled+responses+on+%28calls.action_ptr_id%3Dresponses.whipcountaction_id%29%0A++++where%0A++++++++page.name+%3D+%7B%7B+page+%7D%7D%0A++++++++%0A++++group+by+1%0A++++having%0A++++++++%28supportive%2Fcalls+%3E%3D+%7B%7B+minimum_response_agreement+%7D%7D%0A++++++++++or+opposed%2Fcalls+%3E%3D+%7B%7B+minimum_response_agreement+%7D%7D%0A++++++++++or+uncommitted%2Fcalls+%3E%3D+%7B%7B+minimum_response_agreement+%7D%7D%29%0A+++++++++and+calls+%3E%3D+%7B%7B+minimum_calls+%7D%7D%0A
[[7659, 0.0, 1.0, 0.0, 1],
 [7682, 1.0, 0.0, 0.0, 1],
 [7725, 0.0, 1.0, 0.0, 1],
 [7878, 1.0, 0.0, 0.0, 1],
 [7944, 0.0, 0.0, 1.0, 1],
 [8060, 1.0, 0.0, 1.0, 2]]

Note

The Running ActionKit Reports and SQL using the RESTful API section of the manual has more details on creating, running and getting the results of reports.

Using the target ids in the first column, we can match these results to the targets on our Whipcount page and display them for our users.