This is the third tutorial in a series of post for developers on the Gravity Forms API/developer platform. You can find the first two here:

1. An introduction to the Gravity Forms developer platform

2. Gravity Forms API case study: Approvals

Contents

1. Introduction

The Gravity Forms REST-based Web API provides an easy and secure way for developers to integrate with Gravity Forms. This tutorial complements the official Web API documentation with some additional background information, tips and more sample code.

The Gravity Forms API supports create, read, update and delete (CRUD) operations on all Forms and Entries. It can be used to submit forms and it also has some support for aggregated results. So if you’re writing any kind of application that needs remote access to a Gravity Forms installation you’ll need to use the API. For example:

Mobile apps.

Single-page JavaScript or Web apps.

WordPress themes and plugins using AJAX to interact with Gravity Forms from the browser.

Gravity Forms add-ons that need to interact with another Gravity Forms installation.

Desktop applications.

IMPORTANT: The API is different from the API Functions in the GFAPI class. GFAPI is intended to be consumed only by PHP in plugins and themes running in the same installation as Gravity Forms. The API opens up Gravity Forms even further to browser-based and external clients.

2. Security

With the exception of form submissions all the endpoints require authentication. There are two types of authentication and the one you decide to use will depend on the kind of client you’re writing: one for plugins/themes running on the same site as Gravity Forms and a different one for external applications.

If you’re writing a plugin or theme that will access Gravity Forms on the same site then you’ll need to use WordPress cookie authentication. If you’re writing an external client like a mobile, web or desktop app then you should use the Web API’s built-in authentication scheme which is based on OAuth 1.



WordPress Cookie Authentication

The Gravity Forms Web API supports cookie authentication for WordPress users that are logged in. This means that you don’t need to worry about signing requests or handling authentication. The only requirement is that a special nonce must be sent in the URL of every request.

The nonce action should be set to gf_api and sent in the _gf_json_nonce query parameter. You can generate the nonce using the WordPress wp_create_nonce() function:

$nonce = wp_create_nonce( 'gf_api' );

Example URL:

http://mydomain.com/gravityformsapi/forms/1?_gf_json_nonce=a68ed09be1

External Clients: Signature Authentication

The authentication of external applications is necessarily more complex but the implementation need not be difficult.

First, a bit of background information about the authentication scheme. Forms and entries typically contain confidential or sensitive information so in opening up a REST API to external applications we had to ensure that the system was as secure as possible for our customers while still being simple enough for developers to implement. We studied the ways other APIs were protected and decided to base the authentication scheme on a simple implementation of OAuth 1 with HMAC-SHA1 signatures. We chose this for a number of reasons:

It doesn’t require SSL/HTTPS. This is important because a lot of servers are not ready for this.

Neither the private key nor passwords get transferred across the wire.

Each different request requires a different signature. So, for example, although the private key is always the same, the signature used for retrieving an entry is different to the signature required for updating the same entry and different again from the signature used to retrieve a different entry. If the signature is compromised, the impact is limited to one resource and only until the signature expires.

Replay attacks can be mitigated using timestamps.

It’s possible to generate a signature which essentially never expires – useful for certain situations where information is not sensitive. Use wisely!

We didn’t invent it. It’s based on the same approach widely used to protect a number of high-profile APIs such as Amazon S3 and the Twitter API (in addition to other protocols).

Every API request must be signed, but don’t worry, it’s not hard to implement. Unless there’s a very specialised use case it’s unlikely that you’ll need to reinvent the example signing algorithms included in the official Gravity Forms Web API documentation. At the time of writing there are snippets that you can adopt and adapt to your needs for PHP, JavaScript and C#.

Although you may never need to even see a signed URL, for the sake of clarity and completeness here’s what a signed Web API request URL looks like:

http://mydomain.com/gravityformsapi/forms/1?api_key=fd144510ac&signature=Nj6zoDHF0wAxuFynQPFF29U3%2FEE%3D&expires=1424785599

Notice the URL has 3 query arguments:

api_key: the public key expires: the expiration time expressed as a UNIX UTC timestamp signature: a url-encoded HMAC-SHA1 hash of a combination of the public api key, requested API route, HTTP method and expiration time. Example:

{api_key}:{http method}:{route}:{expires}

1234:GET:forms/1/entries:1369749344

IMPORTANT: Keep the private key private

One important thing to bear in mind when planning your application is how to sign the request URLs without compromising the private key. For example, if you’re planning to use JavaScript to consume a remote Gravity Forms Web API directly then you’ll typically need to ensure that the private key is not sent to the browser. This can be tricky because each different request needs to be signed individually. This is a typical and often cited limitation of OAuth 1 and is frequently solved by involving some kind of server side signing process either during the first page request or via AJAX. The demo project included with this tutorial contains an example implementation.

Authorisation

Once the request has been authenticated, either by cookie or signature, it must also be authorised by the WordPress system of roles and capabilities. This is an important difference between the GFAPI PHP functions and the Web API. In the case of GFAPI, the permissions are not checked at all, that’s left up to the consumer. In the case of the Web API however, the site owner can decide how much control is permitted to each user by adding or removing role capabilities. In the case of external applications, in addition to controlling capabilities, site owners must also select a user account for the Web API to impersonate in the Gravity Forms API settings page. Here are the capability mappings for each of the endpoints:

Method Endpoint Capability PUT forms/[ID]/properties gravityforms_create_form POST forms gravityforms_create_form PUT forms/[ID] gravityforms_edit_forms DELETE forms/[ID] gravityforms_delete_forms POST/PUT forms/[ID]/entries gravityforms_edit_entries GET forms/[ID]/entries gravityforms_view_entries GET forms gravityforms_edit_forms GET forms/[ID] gravityforms_edit_forms GET forms/[ID]/results gravityforms_view_entries POST forms/[ID]/submissions none POST/PUT entries gravityforms_edit_entries PUT entries/[ID]/properties gravityforms_edit_entries DELETE entries/[ID] gravityforms_delete_entries

Each of these capabilities can be changed or removed by using the gform_web_api_capability_[endpoint] family of filters.

Example:

add_filter( 'gform_web_api_capability_post_form_submissions', 'filter_gform_web_api_capability_post_form_submissions'); function filter_gform_web_api_capability_post_form_submissions( $capability ) { return 'my_capability_post_form_submissions'; }

Note: if you're using the Add-On Framework you can add capabilities very easily by adding a $_capabilities array as a class variable in your add-on. Each of the capabilities will be made available to WordPress and will appear in the list of capabilities displayed in role management plugins such as Members.



3. Routing

One of the guiding principles of a RESTful API design is that it should be immediately obvious by looking at an endpoint what it will do. Here are the endpoints for GET along with their description which, hopefully, you should find redundant:

GET /forms

Returns a list of all the forms

Returns a list of all the forms GET /forms/1

Returns the Form object for the form with the id of 1.

Returns the Form object for the form with the id of 1. GET /forms/1/entries

Returns the entries for the form with the id of 1

Returns the entries for the form with the id of 1 GET /forms/1/results

Returns the results summary for the form with the id of 1

Returns the results summary for the form with the id of 1 GET /entries/1

Returns the Entry object for the entry with the id of 1

Conversely, it should also be intuitively obvious what the endpoint should be for a given operation without even looking at the documentation. The Web API follows the commonly accepted convention of using POST for creating and PUT for updating (PUT being idempotent) so for example, it should be obvious that if you want to update an entry you'll need to send a PUT request to /entries/[ID]. If you want to delete an entry send a DELETE request to /entries/[ID].

Although it's obvious which endpoint to use it's not so obvious how to send data and how to interpret the response. Fortunately this is documented in two places - in the official Web API documentation and also, often in greater detail, in the documentation for the GFAPI functions. This is because the Web API is a wrapper for the GFAPI functions so they tend to accept the same parameters and produce similar output. If you can't find the answer in the Web API documentation, check the GFAPI documentation. You also have the inline documentation in the GFAPI class which may sometimes be more up-to-date.

4. Sample Code

The sample code for this tutorial consists of two Gravity Forms Add-Ons, both of which use JavaScript to connect to the Web API. If you're like me you've probably already scanned ahead, opened the links, cloned the repositories, read the readme, activated the add-ons and looked for the menu items called Web API demo 1 andWeb API demo 2 in the Forms menu.

gf-api-demo-1: Uses WordPress cookie authentication for logged in users

gf-api-demo-2: Uses signature authentication using URLs that have been pre-signed in PHP to ensure the private key is not compromised.

In each add-on I've demonstrated five uses for the API:

In this tutorial, I first demonstrate each of the endpoints using sample code for WordPress cookie authentication and then at the end I demonstrate how to adapt the add-on into an external client that uses signature authentication to generate pre-signed URLs ready to consume the API of any server.

I've used the Gravity Forms Add-On Framework just because it's an easy way to enqueue the scripts on the appropriate pages. However, the framework is certainly not required and you can output the scripts and variables using any method you like.

Create a Form

The first step is to create the form. We could just import the form using the import/export page but that wouldn't be much fun so let's create it using the API.

The important part of the PHP will enqueue the JavaScript and output the root URL and nonce:

// Enqueue the JavaScript and output the root url and the nonce. public function scripts() { $scripts = array( array( 'handle' => 'gf_web_api_demo_1', 'src' => $this->get_base_url() . '/js/gf-web-api-demo-1.js', 'deps' => array( 'jquery' ), 'version' => $this->_version, 'enqueue' => array( array( 'query' => 'page=gravityformswebapidemo1' ), ), 'strings' => array( 'root_url' => site_url() . '/gravityformsapi/', 'nonce' => wp_create_nonce( 'gf_api' ), ) ), ); return array_merge( parent::scripts(), $scripts ); }

The markup contains a textarea that's pre-populated with the JSON for the form we need to create. The form is a very simple contact form. It has four fields: a text field that we'll use for the name, an email field, a radio button field and a paragraph field for a message.

<div id="demo_step_1"> <p> Sample Form Object </p> <textarea id="sample_form" rows="10" cols="100"><?php echo $sample_form_json; ?></textarea><br /> <button id="create_form_button" class="button button-primary button-large">Create New Form</button> </div> <textarea id="response" rows="30" cols="100"></textarea>

The JavaScript sends the request to the POST /forms endpoint with the array of form objects that we want to create. The response is an array of Form IDs which we'll use in the next steps.

// get globals apiVars = gf_web_api_demo_1_strings; $('#create_form_button').click(function () { url = apiVars['root_url'] + 'forms?_gf_json_nonce=' + apiVars['nonce']; createForm( url ); }); function createForm(url){ var formJSON = $('#sample_form').val(); // The POST /forms endpoint expects an array of form objects var forms = new Array(JSON.parse(formJSON)); $.ajax({ url: url, type: 'POST', data: JSON.stringify(forms) }) .done(function (data, textStatus, xhr) { // The response contains an array of Form IDs. $('#form_id').val(data.response[0]); }) }

Submit the form

The second example demonstrates how to use the POST forms/[ID]/submissions endpoint to submit a form via the Web API. It shows how to separate Gravity Forms from its front-end markup completely and construct a custom form which submits the values via AJAX, send the notifications, validates the form and display the appropriate confirmation.

Example use cases:

The UI requires markup that's radically different from the markup generated by Gravity Forms. Example, a contract with inputs embedded inside the text. The front-end is not a form. It could be just a button or an action hook triggered at a strategic point in a custom application. Gravity Forms is used to manage entries and display results. A mobile app used by the sales team to register activity and close the sale with a signature from the customer.

The form values will pass through the validation process, send all notifications, process all conditional logic and fire all actions and filters just as if the form was submitted via a form rendered on a page by the Gravity Forms shortcode.

Important: If you're looking to just import or update entries directly without any validation, notifications, confirmation and filters you'll need to use the Entries endpoints. Conversely, if you need to submit a form don't use the Entries endpoints or you'll end up in hook hell trying to fire all the actions and filters in the right places.

This is the simplest of the examples because no authentication is required to submit a form. Here are the pertinent parts of the code.

// Enqueue the JavaScript and generate the variables: root url, form ID, and the nonce. public function scripts() { $scripts = array( array( 'handle' => 'gf_web_api_demo_js', 'src' => $this->get_base_url() . '/js/gf_web_api_demo.js', 'deps' => array( 'jquery' ), 'version' => $this->_version, 'enqueue' => array( array( 'query' => 'page=gravityformswebapidemo' ), ), 'strings' => array( 'root_url' => $this->get_api_url(), 'form_id' => $this->_form_id, 'nonce' => wp_create_nonce( 'gf_api' ), ) ), ); return array_merge( parent::scripts(), $scripts ); }

Form markup:



<form id="gf_web_api_demo_form"> <input id="input_1" name="input_1" type="text" placeholder="Name"/><br/> <input id="input_2" name="input_2" type="text" placeholder="Email"/><br/> <input id="input_3_1" type="radio" name="input_3" value="Information request"/> <label for="input_3_1">I'd like further information about a product</label><br/> <input id="input_3_2" type="radio" class="input_3" name="input_3" value="Complaint"/> <label for="input_3_2">I wish to make a complaint</label><br/> <input id="input_3_3" type="radio" class="input_3" name="input_3" value="Commercial offer"/> <label for="input_3_3">I'm going to try to sell you something</label><br/> <input id="input_3_4" type="radio" class="input_3" name="input_3" value="Just saying hello"/> <label for="input_3_4">I'm an old friend</label><br/> <label for="input_4">Message</label><br/> <textarea id="input_4" name="input_4"></textarea> </form> <div> <button id="submit_button" class="button button-primary button-large">Submit Form</button> </div>

$('#submit_button').click(function () { var url = apiVars['root_url'] + 'forms/' + apiVars['form_id'] + '/submissions'; submitForm( url ); }); function submitForm(url){ var inputValues = { input_1: $('#input_1').val(), input_2: $('#input_2').val(), input_3: $('.input_3:checked').val(), input_4: $('#input_4').val() }; var data = { input_values: inputValues }; $.ajax({ url: url, type: 'POST', data: JSON.stringify(data), beforeSend: function (xhr, opts) { $sending.show(); } }) .done(function (data, textStatus, xhr) { $sending.hide(); var response = JSON.stringify(data.response, null, '\t'); $results.val(response); }) }

Retrieve the latest Entries

JavaScript

The third example show how to retrieve entries for a form using the GET forms/[ID]/entries end point. A successful response contains a collection of Entry objects in JSON.

Example use cases:

Preview latest vacation requests pending the logged-in user's approval. Display the 5 latest comments from customers on the company intranet. A mobile app for searching, displaying and timesheet entries.

Add the button markup:



<button id="get_entries_button" class="button button-primary button-large" >Show Latest Entries</button>



Finally, the JavaScript that will retrieve the JSON collection of Entries and display them in the browser.

$('#get_entries_button').click(function () { formId = $('#form_id').val(); var url = apiVars['root_url'] + 'forms/' + formId + '/entries?_gf_json_nonce=' + apiVars['nonce']; getEntries(url); }); function getEntries(url){ $.ajax({ url: url, type: 'GET', beforeSend: function (xhr, opts) { $sending.show(); } }) .done(function (data, textStatus, xhr) { $sending.hide(); var response = JSON.stringify(data.response, null, '\t'); $results.val(response); }) }

Filter Entries

Entries can be filtered by sending the search parameter.

$('#filter_entries_button').click(function () { formId = $('#form_id').val(); var url = apiVars['root_url'] + 'forms/' + formId + '/entries?_gf_json_nonce=' + apiVars['nonce']; var search = { field_filters : [ { key: '3', value: 'Complaint', operator: 'is' } ] }; url += '&search=' + JSON.stringify(search); getEntries(url); });

For further details about how to filter, page and sort entries, please see the documentation for the GET forms/[ID]/entries endpoint.



Get the Results

The process for retrieving the aggregate results is the same as for the entries. Send a GET request to the forms/[ID]/results endpoint using the appropriate authentication method. A successful response contains a JSON object with the aggregate results for the form.

Example use Cases:

A Status Board Widget that displays the total number of sales today for each product. A management dashboard showing live key performance indicators. Custom UI for Poll or Survey results. You just want the results in JSON so you can build your own UI instead of the built-in chart included in the Polls Add-On.

$('#get_results_button').click(function () { formId = $('#form_id').val(); var url = apiVars['root_url'] + 'forms/' + formId + '/results?_gf_json_nonce=' + apiVars['nonce']; getResults(url); }); function getResults(url){ $.ajax({ url: url, type: 'GET', beforeSend: function (xhr, opts) { $sending.show(); } }) .done(function (data, textStatus, xhr) { $sending.hide(); var response = JSON.stringify(data.response, null, '\t'); $results.val(response); }) }

Bear in mind that the results are cached on the server and the cache can take a while to rebuild if there are a lot of entries. The JSON response includes the following information about the status of the cache which may be useful feedback to give users. For further details please consult the documentation for the results endpoint.



Signature Authentication for External Clients

The WordPress cookie authentication used in the sample code above will only work when the user is logged into the site. An external client requires signature authentication which needs a bit more planning and a bit more code.

The first and foremost concern while planning your application must be to ensure that the private key remains private. Once that private key is compromised the entire Gravity Forms installation is at risk of being compromised.

In the case of mobile and desktop clients, this means storing the private key in a place that cannot be accessed by other applications. In the case of browser-based clients, like an AngularJS app, it means that the private key should probably never leave the server.

The gf-api-demo-2 sample project demonstrates one way to ensure that the private key is not compromised. The URLs are pre-signed on the server and then sent to the browser for use within a limited lifetime. In this case, the signatures will expire after 12 hours but you should keep that duration to the smallest window you can.

Here's some sample code you can use to pre-sign URLs on the server:

// Usage: $get_entries_url = $this->get_pre_signed_url( 'GET', "forms/{$this->_form_id}/entries" ), $get_results_url = $this->get_pre_signed_url( 'GET', "forms/{$this->_form_id}/results" ), private function get_api_url() { $site_url = empty( $this->_api_site_url ) ? site_url() : $this->_api_site_url; return $site_url . '/gravityformsapi/'; } private function get_pre_signed_url( $method, $route, $expires = '+12 hours' ) { $args_array['expires'] = strtotime( $expires ); $args_array['api_key'] = $this->_public_key; $args_array['signature'] = $this->sign( $method, $route, $expires ); $api_url = $this->get_api_url(); $url = add_query_arg( $args_array, $api_url . trailingslashit( $route ) ); return $url; } function sign( $method, $route, $expires = '+12 hours' ) { $expires = strtotime( $expires ); $string_to_sign = sprintf( '%s:%s:%s:%s', $this->_public_key, strtoupper( $method ), $route, $expires ); $hash = hash_hmac( 'sha1', $string_to_sign, $this->_private_key, true ); $sig = rawurlencode( base64_encode( $hash ) ); return $sig; }

IMPORTANT: Signature authentication will work on the same site as the sample project but, unless you really know what you're doing and you really need to use it, this is not recommended for production environments because there's too much unnecessary overhead and complexity.

Note: Cross-Origin Resource Sharing (CORS)

In the case of this demo you can use the same installation as the remote server but of course it will also work when the API url is set to a different server. When you do set it to a different server, initially the JavaScript requests will most likely fail because the remote server needs to be configured to allow cross-origin resource sharing (CORS) in browsers. This is not a tutorial on CORS requests so I'm going to have to skip some details at this point. The easiest and least secure way to do this is to allow all servers to access the server. You can do this by using the following WordPress filter.

add_filter( 'allowed_http_origin', '__return_true' );

If you need more control over the domain that's allowed to call the API you may want to use the handle the pre-flight response yourself, For example:

add_action( 'init', 'handle_preflight' ); function handle_preflight() { // Set the domain that's allowed to make the API call. header("Access-Control-Allow-Origin: " . get_http_origin()); // Set the methods header("Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE"); header("Access-Control-Allow-Headers: Origin, Content-Type, Accept"); if ( 'OPTIONS' == $_SERVER['REQUEST_METHOD'] ) { status_header(200); exit(); } }

I should make it clear that by setting the origin domain on the server it will only affect browser-based applications.



The PHP API Wrapper

Fortunately, if you're writing an external client in PHP (a WordPress plugin or theme) there's an easier way to consume the API. You can use the official Gravity Forms Web API Wrapper class. This ready-to-use client will handle all the authentication leg work for you and provide you with with functions that are very similar to the API methods in GFAPI.

The Web API Wrapper class, GFWebAPIWrapper, consists of the following methods:



get_forms()

get_form( $form_id )

update_form( $form_id, $form )

create_forms( $forms )

create_form( $form )

delete_forms( $form_ids )

delete_form( $form_id )

get_entries( $form_id, $search=null, $sorting=null, $paging=null )

get_entry( $entry_id )

update_entry( $entry_id, $entry )

create_entries( $entries )

create_entry( $entry )

delete_entries( $entry_ids )

delete_entry( $entry_id )

get_results( $form_id



It should be reasonably self-explanatory what each of these functions does with the exception of perhaps the following:

get_forms() returns a list of forms titles and their respective entry counts, not a collection of Form objects.

get_entries() check the inline documentation for details on how to filter, page and sort entries.

To use the API Wrapper just fire up an instance with your credentials in the constructor:



$api = new GFWebAPIWrapper($api_url, $public_key, $private_key)

// returns a Form object

$api->get_form(1);



The sample project includes lots of examples of how to use the API Wrapper so if this is an option for you then stop here, download that project and read through it. The inline documentation has further details for each method.

5. Conclusion

The Gravity Forms API enabled integration with plugins and themes and also from external clients such as mobile, web and desktop applications. Once you realise how Gravity Forms can provide the back-end for any kind of application the possibilities really start to open up. I hope this tutorial helps whets your appetite and gets you thinking about new ways to use it. I'd love to hear about your ideas.

I’ll be very happy to answer any questions you have about this tutorial in the comments. Please direct all specific questions about your project to the Gravity Forms technical support team.

********