NAV
Frontend Backend (Ruby) Backend (PHP)

Introduction

Contact State is a regulatory technology platform specifically designed to build trust in two-sided markets like lead generation and affiliate marketing.

Contact State Certify allows Data Sellers to generate Certificates that serve as a record of provenance for consumer data, and then pass these certificates onto Data Buyers. This provides a trusted reference point for Data Buyers and Data Sellers.

Contact State Connect is an API abstraction layer that sits on top of Data Buyers sales APIs, providing duplicate detection, data validation and lead delivery control to Data Buyers, and a single point of integration to Data Sellers.

Certify

The Certify product integration is the easiest way to generate trusted Certificates.

Certificates are generated in a two-part, real time process:

  1. Generation (frontend): The cscertify javascript library (loaded in the consumers browser) attaches itself to the form submit handler and gets triggered when a user presses the submit button. This makes a call to Contact State and a Claim URL is returned. The frontend then has to pass this Claim URL to the backend.

  2. Claiming (backend): The Certificate is claimed by making a POST request to the Claim URL with the correct Secret Key. The claiming process returns a Certificate URL which can be passed to the Data Buyer, along with other form data. Certificates are claimed for a particular Data Buyer and once claimed will be visible in the Data Buyers Contact State platform.

Example integration code can be found in this document and additionally at https://github.com/contactstate/certify-integration-examples

Credentials

In order to continue with the integration you will need credentials configured within Contact State.

To get your credentials please email support@contactstate.com, supplying the landing page URL Contact State Certify will be integrated on.

  1. Landing Page ID - Provided by Contact State. This identifies the landing page that your form is setup on.

  2. Secret Key - Provided by Contact State. This is used to sign claim requests on behalf of a particular Data Buyer. Important: A Secret Key is unique to a Data Buyer. If you are claiming Certificates for multiple Data Buyers with Contact State you will need multiple secret keys.

Standard Frontend Integration

There are two methods of Frontend integration: Standard and Advanced. Standard is for simple HTML forms that submit through the form action, without using javascript to validate or submit data.

<!-- Embed this code on every page of your website -->
<script>
  (function (w,d,s,o,f,js,fjs) {
    w['ContactStateCertify']=o;w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
    js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
    js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
  }(window, document, 'script', 'cscertify', 'https://js.contactstate.com/certify-latest.js'));
  cscertify('init', { landing_page_id: 'REPLACE_ME_WITH_YOUR_LANDING_PAGE_ID' });
  // In the standard integration, we can attach Contact State directly to the form using the attach call
  cscertify('attach', { form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID' });
</script>

To complete this integration you will need:

  1. Landing Page ID - The Landing page ID you will have received from Contact State.

  2. Form ID - The HTML element ID of the form you wish to attach to. In the following example snippet <form id="testForm" action="/backend" method="POST"> the form_id is testForm.

Attaching to the form

The Standard integration works through a single call to cscertify('attach', args), passing in the form ID as an argument.

The frontend embed code should be placed at the bottom of each page on the website. The Contact State javascript embed will now work unobtrusively with your form.

The cscertify javascript will append a new hidden element to your form automatically called contact_state_claim_cert_url which will be submitted with the form.

Loading, success and failure callbacks

You can customise the integration further through these optional callbacks:

<script>
  // (function (w,d,s...
  // cscertify('init'...
  cscertify('attach', { form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID', 
    loading: function() {
      // This gets called before the submission to Contact State
      document.getElementById('submit-btn').value = 'Verifying with Contact State...';
    }, 
    success: function(claimUrl) {
      // This gets called after the Contact State submission (either success or failure),
      // and just before the form gets submitted as normal.
      document.getElementById('submit-btn').value = 'Submitting with Claim URL...';

      console.log('Claim URL is ' + claimUrl);
    },
    failure: function(err) {
      // The form will be automatically submitted if there is an error!
      // However, this callback is useful for debugging
      document.getElementById('submit-btn').value = 'Submitting without Claim URL...';

      console.log('Something went wrong with the Contact State submission: ' + err);
    }
  });
</script>

loading: This callback is executed before the submission to Contact State. We recommend setting a loading state on your form.

success: This callback is executed after the Contact State submission, for successful requests, and just before the form submission.

failure: This callback is executed on error, when something has gone wrong with the Contact State submission, and just before the form submission. An error object is passed to the function as the first argument with more detail.

Error handling

If the Contact State service is unavailable the form submission process will continue as normal, without the hidden field contact_state_claim_cert_url being submitted.

Additionally, further information on the error is provided in the error callback.

Advanced Frontend Integration

The Advanced integration works in a similar fashion to the standard integration, except you have to manually trigger a call to cscertify('send') in the correct place.

<!-- Embed this code on every page of your website -->
<script>
  (function (w,d,s,o,f,js,fjs) {
    w['ContactStateCertify']=o;w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
    js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
    js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
  }(window, document, 'script', 'cscertify', 'https://js.contactstate.com/certify-latest.js'));
  cscertify('init', { landing_page_id: 'REPLACE_ME_WITH_YOUR_LANDING_PAGE_ID' });
</script>

To complete this integration you will need:

  1. Landing Page ID - The Landing page ID you will have received from Contact State.

  2. Form ID - The HTML element ID of the form you wish to attach to. In the following example snippet <form id="testForm" action="/backend" method="POST"> the form_id is testForm.

Configuring the javascript submit handler

The frontend embed code should be placed at the bottom of each page on the website.

In the javascript form submit handler, before form submission, a call will need to be made to cscertify('send') to retrieve the Claim URL. This URL should then be added to the submit payload and the form should be submitted as normal.

// And then you can put this code in your form submit handler

// Option 1. With callbacks
cscertify('send', { form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID', 
  loading: function() {
    // This gets called before the submission to Contact State
    document.getElementById('submit-btn').value = 'Verifying with Contact State...';
  }, 
  success: function (claimUrl) {
    document.getElementById('submit-btn').value = 'Submitting form...';
    console.log("Page script: Claim URL is %s", certUrl);
    // Ajax request to backend to submit
  }, 
  failure: function (err) {
    console.error('Page script: There was an error!', err)
    // Ajax request to backend to submit
  } 
});

// Option 2: Using Promises (no loading state)
cscertify('send', { form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID' })
.then(function (claimUrl) {
  console.log("Page script: Claim URL is %s", claimUrl);
  // Ajax request to backend to submit
})
.catch(function (err) {
  console.error('Page script: There was an error!', err)
  // Ajax request to backend to submit
});

// Option 2: Using Promises (with a loading state)
new Promise(function (resolve, reject) {
  // Update the loading UI
  document.getElementById('submit-btn').disabled = true;
  resolve()
}).then(function() {
  return cscertify('send', { form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID' });
}).then(function (claimUrl) {
  // Debugging
  console.log("Page script: Cert URL is %s", claimUrl);

  // Updating the UI for the user
  document.getElementById('submit-btn').disabled = false;

  // Ajax request to backend
})
.catch(function (err) {
  console.error('Page script: Augh, there was an error!', err)
  document.getElementById('submit-btn').disabled = false;

  // Ajax request to backend to submit
});

Loading, success and failure callbacks

The Advanced integration is controlled through these callbacks:

loading: This callback is executed before the submission to Contact State. We recommend setting a loading state on your form.

success: This callback is executed after the Contact State submission, for successful requests, and just before the form submission.

failure: This callback is executed on error, when something has gone wrong with the Contact State submission, and just before the form submission. An error object is passed to the function as the first argument with more detail.

Error handling

If the Contact State service is unavailable you can catch the failure (either by promise or callback).

We recommend submitting your form as normal in these blocks.

Advanced Frontend Integration jQuery example

This is an example of an advanced integration using jQuery.

var form = $('form');
form.on('submit', function(e) {
  e.preventDefault();

  // Own logic, Validations etc.
  // ...

  // Submit the form to Contact State to receive a Certificate claim URL
  cscertify('send', {
    form_id: 'REPLACE_ME_WITH_YOUR_FORM_ID',
    loading: function() {
      $('#submit-btn').val('Verifying with Contact State...');
    },
    success: function(claim_url) {
      // You need to pass claim_url to your server backend
      // In this example we're adding it back to the form,
      // then serializing the form and submitting it in an ajax request
      $('<input>').attr({
        type: 'hidden',
        id: 'contact_state_claim_cert_url',
        name: 'contact_state_claim_cert_url',
        value: claim_url
      }).appendTo(form);

      // Set a status for the user
      $('#submit-btn').val('Submitting form...');

      // Submit to your backend as normal
      // The payload will now also include a field called
      // 'contact_state_claim_cert_url' with the Claim URL
      $.ajax({
        type : 'POST',
        data: form.serialize(),
        url: '/path/to/backend',
        dataType : 'json',
        success: function(res){}
      });
    },
    failure: function() {
      // Set a status for the user
      $('#submit-btn').val('Submitting form...');

      // We've failed to get a Contact State Claim URL
      // It's a good idea to submit your form anyway
      // so that the form data is not lost
      $.ajax({
        type : 'POST',
        data: form.serialize(),
        url: '/path/to/backend',
        dataType : 'json',
        success: function(res){}
      });
    }
  });
});

By default Certify.js does not set cookies in the consumer browser.

Enabling Certify.js to work with cookies unlocks cookie based analytics, and to turn this on requires consumer consent.

Once consent has been obtained, a call to cscertify('cookies') will enable it in Certify.js.

// Call to turn cookie analytics on
cscertify('cookies', { consumer_consent: true });

// Alternatively, a function can be provided
var cookieConsent = function() {
  return confirm("Cookies allowed?");
}
cscertify('cookies', { consumer_consent: cookieConsent });

// Here's an example with an off the shelf 
// cookie preferences tool (Cookiebot)
window.addEventListener('CookiebotOnAccept', function (e) {
  cscertify('cookies', { consumer_consent: true });
}, false);

Privacy notice

<script>
  (function (w,d,s,o,f,js,fjs) {
    w['ContactStateCertify']=o;w[o] = w[o] || function () { (w[o].q = w[o].q || []).push(arguments) };
    js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
    js.id = o; js.src = f; js.async = 1; fjs.parentNode.insertBefore(js, fjs);
  }(window, document, 'script', 'cscertify', 'https://js.contactstate.com/certify-latest.js'));
  cscertify('init', { landing_page_id: 'REPLACE_ME_WITH_YOUR_LANDING_PAGE_ID' });
  // Append the following line to the standard or advanced embed code
  cscertify('widget');
</script>

It's mandatory to inform the consumer that Contact State is operating on the form before they press the submit button.

By including the <div> container in the right place, Contact State can automatically inject this text.

Here's an example:

<!-- Privacy notice -->
<div id="contact-state-privacy-notice"></div>

Backend Integration

The goal of this step is to turn a Claim URL into a Certificate URL which can then be passed to the Data Buyer. Claiming a Certificate is mandatory before any Contact State scoring is performed on the data.

Example integration code can be found in this document and additionally at https://github.com/contactstate/certify-integration-examples

To complete this integration you will need:

  1. Secret Key - Provided by Contact State. This is used to sign claim requests on behalf of a particular Data Buyer. Important: A Secret Key is unique to a Data Buyer. If you are claiming Certificates for multiple Data Buyers with Contact State you will need multiple Secret Keys.

  2. A Claim URL - Passed from the frontend through either a Standard or Advanced integration.

Claiming a Certificate

To claim a Certificate, a POST request needs to be made to the Claim URL, passing in the secret key in the request body in a data element called secret_key.

In a Standard integration, on form submission, your form backend will receive a Certificate Claim URL in a field called contact_state_claim_cert_url.

In an Advanced integration, what this field is called it determined by you, although we would recommend calling it contact_state_claim_cert_url.

See the Ruby and PHP examples provided.

Successful response

A successful claim will return with a HTTP status code of 201 and the following JSON:

{

"cert_url": "https://cert.contactstate.com/certs/:id",

"reclaim_cert_url": "https://cert.contactstate.com/certs/:claim_token/claim"

}

The cert_url value is the Certificate URL. This can be saved and passed along to the Data Buyer with the rest of the form data.

The reclaim_cert_url value enables another claim to be made, to assign the certificate from a Seller account to a Buyer account.

Reclaiming a Certificate

Certificates generated against a Seller account can be reassigned to a Buyer account within 15 minutes of generation.

This is potentially useful in situations where it's initially unclear which Buyer to assign a Certificate to.

The reclaiming process is the same as the claim process, a HTTP POST has to be made to the reclaim_cert_url with the Secret Key matching the new Sales Pipe you wish to assign to.

Please note: a reclaim can only be made if the certificate was initially claimed against a Seller account. Certificates already attached to Sales Pipes cannot be reclaimed.

Error handling

Errors will be returned with a HTTP status code of 500 and the following JSON:

{ "error": { "message": "Error message" } }

Top tip: Certificates must be claimed within 30 seconds of generation.

# Set your secret key in the variable below
# Your secret key is provided by Contact State
secret_key = 'REPLACE_ME_WITH_YOUR_SECRET_KEY'

# Claim URL is passed in from the form submission
claim_url = params[:contact_state_claim_cert_url]

if claim_url.present?
 # Make a POST request to claim the Certificate
 response = HTTParty.post(claim_url,
   body: {
     secret_key: secret_key
   }.to_json,
   headers: { 'Content-Type' => 'application/json' }
 )

  # Get the certificate URL from the response
  # This should be stored and passed along to the Data Buyer
 certificate_url = JSON.parse(response.body)['cert_url']
 puts "The certificate can be viewed at #{certificate_url}"
else
 # If we get here something has gone wrong with the javascript embed
 raise "No claim URL received" 
end
<?php
  # Set your secret key in the variable below
  # Your secret key is provided by Contact State
  $secret_key = 'REPLACE_ME_WITH_YOUR_SECRET_KEY';

  # Claim URL is passed in from the form submission
  $claim_url = $_POST['contact_state_claim_cert_url'];

  if(isset($claim_url) && $claim_url != '') {
    $content = json_encode(array('secret_key' => $secret_key));

    $curl = curl_init($claim_url);
    curl_setopt($curl, CURLOPT_HEADER, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, $content);

    # Make a POST request to claim the Certificate
    $json_response = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    if ($status != 201) {
      die("Error: call to URL $claim_url failed");
    }
    curl_close($curl);

    # Get the Certificate URL from the response
    # This should be stored and passed along to the Data Buyer
    $response = json_decode($json_response, true);
    $certificate_url = $response['cert_url'];
    echo "The certificate URL is $certificate_url";
  } else {
    # If we get here something has gone wrong with the javascript embed
    die('Claim URL not set');
  }
?>
# Response JSON on success

{
  "cert_url": "https://cert.contactstate.com/certs/:id",
  "reclaim_cert_url": "https://cert.contactstate.com/certs/:claim_token/claim"
}

Data Sellers and Data Buyers can pull data from Contact State into their CRM solution to assist with internal reporting.

Data Buyers can push data from their CRM solution into Contact State to unlock more powerful reporting in the Contact State Platform.

Storing the Certificate URL

We recommend both Data Sellers and Data Buyers store the Certificate URL so they both have the same point of reference when discussing a data record. Storing the Certificate URL is an important prerequisite to further CRM integration for Data Buyers.

Data Sellers can simply store the Certificate URL once it has been generated in their CRM or database solution.

Data Buyers can store the Certificate URL in their own CRM solution, alongside the other consumer data that has been sent from the Data Seller.

We recommend creating a new field CRM or database field called Contact State Certificate URL to store the Certificate URL, which is at least 72 characters long.

Pulling Certificate Data

There are a number of Certificate attributes a Data Seller and Data Buyer may wish store in their own CRM systems to assist with internal reporting, like "scoring status".

Certificates are available as JSON by appending .json to a certificate URL. For example:

https://cert.contactstate.com/certs/ac5e67f3-d280-4209-a2d6-06474d3eae23.json

Important: Certificates are usually scored within one minute, but this process could take several minutes. Certificates sit in a "claimed" state until scoring has been completed. Data Sellers should not wait for scoring to complete to send the Consumer data and the Certificate as we make no guarantees on how long the scoring process will take.

<?php
$cert_url = 'https://cert.contactstate.com/certs/ac5e67f3-d280-4209-a2d6-06474d3eae23.json';

$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $cert_url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$cert_json = curl_exec($curl);
curl_close($curl);
$cert = json_decode($cert_json, true);

echo "Cert ID is " . $cert['data']['id'];
echo "Scoring status is " . $cert['data']['attributes']['status'];

?>
cert_url = 'https://cert.contactstate.com/certs/ac5e67f3-d280-4209-a2d6-06474d3eae23.json'

response = HTTParty.get(cert_url, format: :plain)
cert = JSON.parse(response, symbolize_names: true)

puts "Cert ID is #{cert[:data][:id]}"
puts "Scoring status is #{cert[:data][:attributes][:status]}"
{
  "data": {
  "id": "f7cb639a-2e90-4703-b35c-f416dd68573e",
  "type": "contact-state-certificate",
  "attributes": {
    "url": "https://cert.contactstate.com/certs/f7cb639a-2e90-4703-b35c-f416dd68573e",
    "status": "certified",
    "scored_at": "2019-10-05T15:16:33.577+01:00",
    # ...
    }
  }
}

Connect

Contact State Connect is the easiest way to deliver leads in a secure and compliant way.

The API is an abstraction layer on top of lead buyer sales APIs, databases and CRM systems. A single integration with Contact State Connect allows lead delivery to multiple lead buyers, with no additional integration work per buyer.

Contact State Connect sits alongside and works with Contact State Certify, so that every lead comes with a certificate.

Changelog

2021-04-29 - Initial version released

2021-07-01 - Delete methods added

2022-02-15 - Added 'contact_time' to available fields

2022-03-09 - Added 'cost' to available fields

2023-01-06 - Added 'consent_marketing_whatsapp' to available fields

2024-01-15 - Added 'advert_code' to available fields

Getting started

API Console

The API console can be found at https://connect.contactstate.com/console

The console provides an easy to use UI to create API calls, as well as defining a set of Swagger documentation whuch includes example payloads and schemas. Alongside this document, the console is the easiest way to get familiar with the Connect API and try it out.

Example code

There is example code demonstrating how to use the Connect API at https://github.com/contactstate/connect-integration-examples

Authentication

Authentication is required for all calls to Contact State Connect.

The authentication mechanism is an API key. This can be found within your Contact State account.

The API key is set as an HTTP 'Authorization' header with all requests.

Authorization: Bearer your-api-key-goes-here

Sending data

Data can be sent to the API as application/json or application/x-www-form-urlencoded (a regular form POST).

The API is reasonably permissive in terms of what it accepts. All data values should be sent as strings, and date values should be sent in the format yyyy-mm-dd. Validation rules, if required, are specified in this document.

API methods

POST /leads - Creating a lead

POSTing to the /leads endpoint creates a new lead in Contact State Connect:

POST /leads

curl -X POST "https://connect.contactstate.com/leads"
     -H  "accept: application/json"
     -H  "Authorization: Bearer $api-key"
     -H  "Content-Type: application/json"
     -d "{\"first_name\":\"John\",\"last_name\":\"Smith\", \"email\":\"support@contactstate.com\",\"home_phone\":\"07887898900\"}"

See the fields section for allowed fields to send.

Response

If successful, the endpoint will respond with HTTP code 201 (created) and metadata about the lead, which includes an ID to refer to it in subsequent requests.

See Error States for documentation on unsuccessful requests.

GET /leads/:id - Retrieving a lead

Data about a lead can be retrieved by making an API request that looks like this:

GET /leads/:id

Where :id is the Contact State Connect id of the lead (this will be returned in the create request response payload).

Response

If successful, the endpoint will respond with HTTP code 200 and with all the lead fields that were initially sent, as well as additional data about the delivery.

DELETE /leads/:id - Deleting a lead

A lead can be deleted from Contact State's platform by making an API request that looks like this:

DELETE /leads/:id

Where :id is the Contact State Connect id of the lead (this will be returned in the create request response payload).

Response

If successful, the endpoint will respond with HTTP code 200 and with a success status.

DELETE /leads - Deleting multiple leads

Multiple leads (up to a maximum of 25 per request) can be deleted from Contact State's platform by making an API request that looks like this:

DELETE /leads

with a JSON payload of

{ "lead_ids": ["lead-id-1", "lead-id-2"] }

Where "lead_ids" is an array of Contact State Connect lead ids.

Response

If successful, the endpoint will respond with HTTP code 200 and with a success status.

Error states

The main error states are arranged by HTTP status code.

Any HTTP status from 200 - 299 is a successful response.

HTTP statuses in the 400 - 499 range represent user error and need corrective action to be taken.

A HTTP status of 500 or more is an API error and requests should be retried later.

All API logs can be found within your Contact State account.

Error object

All errors from the API are returned in an error object which looks like this:

{"error_type": "invalid_request_error", "errors": []}

The errors item contains an array of errors, each of which look like this:

{"code": "validation", "message": "must be filled", "param": "first_name"}

The param field will either be null or contain a field name. The error message is designed to be displayable to the end user. When the param field is null, the error belongs to the entire request.

400 - Invalid request

{
  "error_type": "invalid_request_error",
  "errors": [
    {
      "code": "invalid_parameters",
      "message": "The request payload is incorrectly formatted, empty, or contains invalid parameters.",
      "param": null
    },
    {
      "code": "validation",
      "message": "must be filled",
      "param": "first_name"
    }
  ]
}

Returns with a HTTP status of 400.

Occurs when something is wrong with the request payload and a lead cannot be created.

401 - Authentication failed

{
  "error_type": "authentication_error",
  "errors": [
    {
      "code": "invalid_credentials",
      "message": "Invalid credentials",
      "param": "Authorization Bearer token"
    }
  ]
}

Returns with a HTTP status of 401.

Occurs when the API key is missing or invalid.

404 - Resource missing

{
  "error_type": "resource_missing_error",
  "errors": [
    {
      "code": "lead_not_found",
      "message": "Lead with id='missing-lead-id' not found",
      "param": null
    }
  ]
}

Returns with a HTTP status of 404.

Occurs when the resource in question cannot be found at the URL specified.

429 - Rate limit error

{
  "error_type": "rate_limit_error",
  "errors": [
    {
      "code": "rate_limit_exceeded",
      "message": "Rate limit exceeded, 0/10 remaining. Check header RateLimit-Reset for next retry time.",
      "param": null
    }
  ]
}

Returns with a HTTP status of 429.

The Connect API is actively rate limited. If you send more than 60 requests/minute you will be rate limited.

If you send more than 10 requests/minute to the leads creation endpoint you will be rate limited.

The following HTTP headers are returned when you hit a rate limit:

Header Description Type
RateLimit-Limit The number of allowed requests in the current period integer
RateLimit-Remaining The number of remaining requests in the current period integer
RateLimit-Reset UNIX timestamp of next retry time integer

You can use the value in RateLimit-Reset to set the next retry time.

500 - Internal server error

Returns with a HTTP status of 500.

Occurs when something has gone wrong within Contact State Connect.

{
  "error_type": "api_error",
  "errors": [
    {
      "code": "internal_server_error",
      "message": "Something went wrong",
      "param": null
    }
  ]
}

503 - Maintenance

Returns with a HTTP status of 503.

Occurs when Contact State Connect is undergoing maintenance.

The HTTP body could be either be a HTML or JSON response and should be parsed carefully.

JavaScript integration

If it is enabled on your Contact State account, it is possible to integrate with the Connect lead delivery API through our JavaScript library.

A full integration example with jQuery can be found in the Connect integration examples Github repo.

<!-- Embed code to be put in your HTML page -->
<script>  
  window.ContactState = window.ContactState || {};
  ContactState.q = ContactState.q || [];
  function csconfig(){ContactState.q.push(arguments)};
  csconfig('init', {landing_page_id: 'YOUR_LANDING_PAGE_ID'});
</script>
<script async src="https://js.contactstate.com/contactstate-latest.js"></script>
// Javascript function to be put in your form submit handler
ContactState.Connect.create({
  api_key: 'YOUR_FRONTEND_API_KEY_HERE',
  // Toggle the certify flag to true to generate a certificate for this request.
  certify: true,
  fields: {
    contact_state_connect_code: 'YOUR_CONNECT_CODE',
    // Map the fields on the form to the API
    // Each field on the left corresponds directly to API fields 
    // found at https://docs.contactstate.com/#fields
    first_name: "Jane",
    last_name: "Smith",
    email: "jane.smith@example.com",
    home_phone: "01220 000000",
    message: "Testing!",
  }
}).then(function(response) {
  // response is a successful API response object
  console.log(response);
}).catch(function(errors) {
  // errors is an array of errors from the API
  console.log(errors);
});

Fields

By default, the only three required fields to create a lead are: first_name, last_name and email.

However, if you are delivering a lead through a distribution group then we require you to send all of the fields that the end buyers within the delivery distribution group require. Failure to provide these will result in a validation error.

In addition, what is enforced by default is only a base. You can create your own validation rules for each field. Please see the validation rules section for more information.

For simplicity, all fields are sent through a flat JSON structure (if application/json) or a non-nested params structure (if application/x-www-form-urlencoded).

Consumer fields

{
  "title"                     :"Ms",
  "first_name"                :"Jane",
  "middle_name"               :"Penny",
  "last_name"                 :"Smith",
  "company_name"              :"ACME Ltd",
  "date_of_birth"             :"1970-05-10",
  "gender"                    :"Female",
  "home_phone"                :"01223 444555",
  "mobile_phone"              :"078 9990 0122",
  "business_phone"            :"01135 776567",
  "email"                     :"jane.smith@example.com",
  "alt_email"                 :"jane.smith@work.example.com",
  "contact_time"              :"Morning",
  "address_line1"             :"The Hill",
  "address_line2"             :"My Street",
  "city"                      :"London",
  "county"                    :"Greater London",
  "state"                     :"England",
  "country"                   :"United Kingdom",
  "postal_code"               :"N1 6AU",
  "twitter"                   :"https://www.twitter.com/example-jane-smith",
  "linkedin"                  :"https://www.linkedin.com/in/example-jane-smith",
  "facebook"                  :"https://www.facebook.com/example-jane-smith",
  "skype"                     :"example-jane-smith",
  "alt_social"                :"https://www.bebo.com/example-jane-smith"
}
Field Validation Description
title Optional. If specified, must be one of: Mr, Mrs, Ms, Miss, Mx, Dr, Other Title
first_name Required First name
middle_name Optional Middle name
last_name Required Last name
company_name Optional Company name
date_of_birth Optional. If specified, must be in 'yyyy-mm-dd' format. Date of birth
gender Optional. If specified, must be one of: Male, Female, Non-binary, Transgender, Intersex, Not disclosed Gender
home_phone Optional Home phone number
mobile_phone Optional Mobile phone number
business_phone Optional Business phone number
email Required Primary email address
alt_email Optional Alternative email address
contact_time Optional. If specified must either be a yyyy-mm-dd date or one of: Anytime, Morning, Afternoon, Evening, Today, Tomorrow, This week, Next week Preferred contact time
address_line1 Optional Address line 1
address_line2 Optional Address line 2
city Optional City
county Optional County
state Optional State
country Optional Country
postal_code Optional Postal code
twitter Optional Twitter profile URL
linkedin Optional Linkedin profile URL
facebook Optional Facebook profile URL
skype Optional Skype username
alt_social Optional Alternative social media name

Product fields

{
  "product_type"              :"Life",
  "product"                   :"Life with critical illness",
  "employment_status"         :"Employed full-time",
  "employment_title"          :"Developer",
  "annual_income"             :"25000",
  "house_value"               :"250000",
  "credit_grade"              :"Good",
  "credit_missed_payments"    :"No",
  "cover_for"                 :"Me",
  "cover_existing"            :"Yes",
  "cover_required_from"       :"2021-06-01",
  "cover_type"                :"LT",
  "cover_amount"              :"300000",
  "cover_additional_amount"   :"100000",
  "cover_term"                :"20",
  "cover_cancer"              :"Yes",
  "cover_smoker"              :"No",
  "company_employees"         :"5",
  "mortgage_balance"          :"100000",
  "mortgage_required"         :"200000",
  "mortgage_deposit"          :"50000",
  "mortgage_term"             :"25",
  "mortgage_deal_expiry"      :"2025-03-20"
}
Field Validation Description
product_type Optional. If specified, must be one of: No product, Corporate Health, Relevant Life, Equity Release, Funeral Planning, Health, Income Protection, Life, Mortgage Product type being sold to consumer
product Optional. Specific product being sold to the consumer
employment_status Optional. If specified, must be one of: Not employed, Employed full-time, Employed part-time, Contractor, Freelancer, Self-employed, Business owner, Carer, Retired Employment status
employment_title Optional Job title
annual_income Optional Annual income
homeowner Optional. If specified, must be one of: Yes, No Is the consumer a homeowner?
house_value Optional House value
credit_grade Optional. If specified, must be one of: Poor, Fair, Good, Very good, Exceptional Credit grade
credit_missed_payments Optional. If specified, must be one of: Yes, No Has the consumer missed any payments?
cover_for Optional. If specified, must be one of: Me, Myself and partner, My family, Other Who is the cover for?
cover_existing Optional. If specified, must be one of: Yes, No Does the consumer have existing cover?
cover_required_from Optional. If specified, must be a date in the format of 'yyyy-mm-dd' When is the cover required from?
cover_type Optional, must be one of "LT", "DT" LT = Level Term, DT = Decreasing Term
cover_amount Optional How much cover does the consumer require?
cover_additional_amount Optional Additional cover required (e.g. critical illness)
cover_term Optional How many years of cover?
cover_cancer Optional. If specified, must be one of: Yes, No Cancer cover?
cover_smoker Optional. If specified, must be one of: Yes, No Is the consumer a smoker?
company_employees Optional How many employees at your company?
mortgage_balance Optional Balance on mortgage remaining.
mortgage_required Optional Amount required for mortgage.
mortgage_deposit Optional Mortgage deposit available.
mortgage_term Optional Term in years for the mortgage.
mortgage_deal_expiry Optional When the current mortgage deal expires.

Applicant 2

These fields allow you to specify data on additional applicants.

{
  "applicant2_title"             :"Mr",
  "applicant2_first_name"        :"John",
  "applicant2_middle_name"       :"David",
  "applicant2_last_name"         :"Smith",
  "applicant2_email"             :"john.smith@example.com",
  "applicant2_date_of_birth"     :"1975-05-20",
  "applicant2_gender"            :"Male",
  "applicant2_cover_smoker"      :"No"
}
Field Validation Description
applicant2_title Optional. If specified, must be one of: Mr, Mrs, Ms, Miss, Mx, Dr, Other Title
applicant2_first_name Optional First name
applicant2_middle_name Optional Middle name
applicant2_last_name Optional Last name
applicant2_email Optional Email address
applicant2_date_of_birth Optional. If specified, must be in 'yyyy-mm-dd' format. Date of birth
applicant2_gender Optional. If specified, must be one of: Male, Female, Non-binary, Transgender, Intersex, Not disclosed Gender
applicant2_cover_smoker Optional. If specified, must be one of: Yes, No Is applicant 2 a smoker?

Marketing fields

{
  "advert_code"               :"acbd1234",
  "marketing_campaign"        :"promo-1",
  "marketing_keyword"         :"life-cover",
  "marketing_medium"          :"ppc",
  "marketing_source"          :"facebook",
  "marketing_website_url"     :"https://www.example.com/life-cover-for-you"
}
Field Validation Description
advert_code Optional Advert code
marketing_campaign Optional Marketing campaign
marketing_keyword Optional Marketing keyword
marketing_medium Optional Marketing medium
marketing_source Optional Marketing source
marketing_website_url Optional Website URL
{
  "consent_marketing"          :"No",
  "consent_marketing_email"    :"Yes",
  "consent_marketing_phone"    :"No",
  "consent_marketing_sms"      :"Yes",
  "consent_marketing_mail"     :"Yes",
  "consent_marketing_whatsapp" :"Yes"
}
Field Validation Description
consent_marketing Optional, Yes/No Consent for all marketing
consent_marketing_email Optional, Yes/No Consent for email marketing
consent_marketing_phone Optional, Yes/No Consent for phone marketing
consent_marketing_sms Optional, Yes/No Consent for SMS marketing
consent_marketing_mail Optional, Yes/No Consent for mail marketing
consent_marketing_whatsapp Optional, Yes/No Consent for Whatsapp marketing

Technical fields

{
  "seller_internal_id"        :"12345",
  "ip_address"                :"192.168.1.1",
  "user_agent"                :"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36",
  "cost",                     :"25.50",
  "click_id"                  :"123xyz",
  "click_id_type"             :"gclid",
  "message"                   :"Message from the consumer or lead seller to the buyer",
  "notes"                     :"Internal notes",
  "alt1"                      :"Alternative field 1, use for anything",
  "alt2"                      :"Alternative field 2, use for anything",
  "alt3"                      :"Alternative field 3, use for anything"
}
Field Validation Description
seller_internal_id Optional Internal ID of the lead in the seller system
ip_address Optional IP address
user_agent Optional Browser user agent
cost Optional Lead cost
click_id Optional Click ID
click_id_type Optional. If specified, must be one of: gclid, gbraid, wbraid, msclkid, fbclid, Other Click ID type
message Optional Message from consumer, or message from seller to buyer.
notes Optional Internal notes for seller. Not shared with buyers.
alt1 Optional Alternative field 1. Use this for anything.
alt2 Optional Alternative field 2. Use this for anything.
alt3 Optional Alternative field 3. Use this for anything.

Contact State fields

{
  "contact_state_connect_code"    :"JHTZ0HNAL5",
  "contact_state_test_mode"       :"true",
  "contact_state_cert_url"        :"https://cert.contactstate.com/certs/534ede1d-9430-4bc9-8aa5-6626d2ab5a12",
  "contact_state_claim_url"       :"https://cert-gen.contactstate.com/certs/534ede1d-9430-4bc9-8aa5-6626d2ab5a12/claim",
  "contact_state_landing_page_id" :"7739a7b8-1710-46cf-99d7-44fb7b5cc76e"
}
Field Validation Description
contact_state_connect_code Optional Specifies where the lead is delivered. Connect codes are attached to distribution groups.
contact_state_test_mode Optional, true/false. If true it will route the delivery to the endpoints test environment.
contact_state_cert_url Optional. Must be in cert URL format. Certificate URL from Certify integration.
contact_state_claim_url Optional. Must be in claim URL format. To be used if Contact State is claiming this URL on your behalf.
contact_state_landing_page_id Optional. If contact_state_claim_url is specified then it is required. ID of the landing page this lead came from.

Validation

Validation rules on fields can be configured within your Contact State account, on an account wide basis, and/or on a distribution group basis.

There are a number of in-built rules that are enabled with a click, and custom rules which can also be created.

In-built rules:

Rule Description
Obscenity filter checks the field for obscenity
Fake data filter checks the field for test data
Real email check to see if the email address is real
Real phone check to see if the phone number address is real
Real postal code check to see if the postal code is real

Custom validation rules:

Rule Description
be present verifies the value is present
be equal if present, verifies equality with a specified value
not be equal if present, verifies non-equality with a specified value
match if present, verifies a substring match with a specified value
not match if present, verifies a non substring match with a specified value
include comma delimited list of values to equality match against
exclude comma delimited list of values to exclude
be in the format of allows a regular expression to be specified to match against
be an email address checks it is in the right format for an email
be a phone number checks the format of the input against UK phone numbers

Delivery

Distribution groups

By default, leads sent to the Connect API are not delivered to buyers until a Connect code is specified with the contact_state_connect_code field.

Connect codes map to distribution groups, and distribution groups map to multiple buyers who will receive the lead.

Certificates and auto-claiming

Generated certificates can be passed through the contact_state_cert_url field.

If you would like the distribution API to claim a certificate for you (this is required when delivering to a distribution group with more than 1 buyer) then simply pass the certificate claim URL in the contact_state_claim_url field.

When passing a contact_state_claim_url, it's also important that you pass a contact_state_landing_page_id too.

Docs for how to generate a claim URL can be found at http://docs.contactstate.com

Notifications

Notifications can be configured in the platform to trigger when certain events happen. Currently, these events are:

Event Description
lead_created The lead has been created. i.e. the POST to /leads has been successful.
delivery_success The lead has been delivered to the buyer
delivery_failure The delivery has failed

Notification methods can be setup and attached to each event. We currently support:

Method Description
Slack sends a message to Slack
Email sends en email
SMS sends an SMS
Webhooks sends a webhook

Webhooks

{
  "event"   :"delivery_success",
  "lead"    :{
    "address_line1"             :"The Hill",
    "address_line2"             :"My Street",
    "alt1"                      :"Alternative field 1, use for anything",
    "alt2"                      :"Alternative field 2, use for anything",
    "alt3"                      :"Alternative field 3, use for anything",
    "alt_email"                 :"jane.smith@work.example.com",
    "alt_social"                :"https://www.bebo.com/example-jane-smith",
    "annual_income"             :"25000",
    "applicant2_title"          :"Mr",
    "applicant2_first_name"     :"John",
    "applicant2_middle_name"    :"David",
    "applicant2_last_name"      :"Smith",
    "applicant2_email"          :"john.smith@example.com",
    "applicant2_date_of_birth"  :"1975-05-20",
    "applicant2_gender"         :"Male",
    "applicant2_cover_smoker"   :"No",
    "business_phone"            :"01135 776567",
    "city"                      :"London",
    "company_employees"         :"5",
    "company_name"              :"ACME Ltd",
    "consent_marketing"         : "No",
    "consent_marketing_email"   : "Yes",
    "consent_marketing_phone"   : "No",
    "consent_marketing_sms"     : "No",
    "consent_marketing_mail"    : "No",
    "contact_state_cert_url"    :"https://cert.contactstate.com/certs/534ede1d-9430-4bc9-8aa5-6626d2ab5a12",
    "contact_state_routing_code":"JHTZ0HNAL5",
    "country"                   :"United Kingdom",
    "county"                    :"Greater London",
    "cover_additional_amount"   :"100000",
    "cover_amount"              :"300000",
    "cover_cancer"              :"Yes",
    "cover_existing"            :"Yes",
    "cover_for"                 :"Me",
    "cover_required_from"       :"2021-06-01",
    "cover_smoker"              :"No",
    "cover_term"                :"20",
    "cover_type"                :"LT",
    "created_at"                :"2021-05-05T08:57:13+01:00",
    "credit_grade"              :"Good",
    "credit_missed_payments"    :"No",
    "date_of_birth"             :"2020-05-10",
    "email"                     :"jane.smith@example.com",
    "employment_status"         :"Employed",
    "employment_title"          :"Developer",
    "facebook"                  :"https://www.facebook.com/example-jane-smith",
    "first_name"                :"Jane",
    "gender"                    :"Female",
    "home_phone"                :"01223 444555",
    "homeowner"                 :"Yes",
    "house_value"               :"250000",
    "id"                        :"71ebc642-0d25-4c6a-84f5-efc22f495ac0",
    "ip_address"                :"192.168.1.1",
    "last_name"                 :"Smith",
    "linkedin"                  :"https://www.linkedin.com/in/example-jane-smith",
    "marketing_campaign"        :"promo-1",
    "marketing_keyword"         :"life-cover",
    "marketing_medium"          :"ppc",
    "marketing_source"          :"facebook",
    "marketing_website_url"     :"https://www.example.com/life-cover-for-you",
    "message"                   :"Message from the consumer or lead seller to the buyer",
    "middle_name"               :"john",
    "mobile_phone"              :"078 9990 0122",
    "mortgage_balance"          :"100000",
    "mortgage_required"         :"200000",
    "notes"                     :"Internal notes",
    "postal_code"               :"N1 6AU",
    "product"                   :"Life",
    "product_type"              :"Life with critical illness",
    "seller_internal_id"        :"12345",
    "skype"                     :"example-jane-smith",
    "state"                     :"England",
    "title"                     :"Ms",
    "twitter"                   :"https://www.twitter.com/example-jane-smith",
    "user_agent"                :"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"
  },
  "deliveries":[{
    "id"                     :"e085fa09-d122-49c1-956a-319c7b2b4609",
    "distribution_group_id"  :"925970ac-6634-4c29-8198-26fce0ae10b6",
    "distribution_group_name":"Test group",
    "buyer_id"               :"1ea0c04b-5dea-427a-b08b-815aeb5c0be3",
    "buyer_name"             :"Buyer 1",
    "endpoint_id"            :"57637c11-1efa-45a5-a8e9-226c79d6c381",
    "endpoint_name"          :"Mock",
    "test_mode"              :true,
    "status"                 :"delivered",
    "delivered_at"           :"2021-05-05T08:57:14+01:00",
    "failed_at"              :null,
    "retried_at"             :null,
    "retry_count"            :0,
    "created_at"             :"2021-05-05T08:57:13+01:00"
  }]
}

Webhooks can be configured to send data to an endpoint of your choosing when an event is triggered.

For example, you might wish to update your database with delivery details when a lead has been delivered successfully.

The payload format is specified on the right and contains three top level keys:

  1. event - the event that the webhook relates to
  2. lead - the lead data
  3. delivery - the delivery data

Delivery statuses can be one of: pending, delivered, pending_retry, failed

Perform

Pushing Sales Data

Data Buyers can push sales data to Contact State from their CRM system. Doing this:

To complete this integration you will need:

  1. An entity to attach the sales data to. This can be either:

    Connect ID: The Connect ID to which the sent data will be applied. You will receive the Connect ID with the consumer data payload.

    or

    Certificate URL: The Certificate to which the sent data will be applied. You should have completed the "Storing the Certificate URL" step.

  2. Secret Key: This key authorises the Buyer's system to send data. Contact State will supply you with this.

Data fields

# Example method to map an internal CRM status to
# a status that Contact State will accept.
def internal_status_to_contact_state_status(internal_status)
  {        
    'Unassigned' => 'NEW',
    'With advisor' => 'TRANSFER',
    'Quoted' => 'QUOTE',
    'Sale' => 'SOLD',
    'Wrong product' => 'INELIGIBLE',
    'Not Interested' => 'NOT_INTERESTED',
    'Cancelled' => 'CANCELLED',
    'Could not contact' => 'NO_CONTACT',
    'Invalid' => 'INVALID',
    'Wrong phone' => 'INVALID_PHONE',
    'Wrong email' => 'INVALID_EMAIL',
    'Hoax' => 'INVALID_HOAX',
    'Dupe' => 'DUPLICATE',
    'Test' => 'TEST'
  }.fetch(internal_status, 'NEW') # default to 'NEW'
end

# HTTP POST call to push data to Contact State
def update_contact_state(crm_record)
  secret_key = 'REPLACE_ME_WITH_YOUR_SECRET_KEY'

  HTTParty.post('https://perform.contactstate.com/leads',
    body: {
      connect_id: crm_record[:connect_id],
      secret_key: secret_key,
      status: internal_status_to_contact_state_status(crm_record[:status]),
      status_at: crm_record[:sold_at],
      status_by: crm_record[:sold_by],
      received_at: crm_record[:received],
      internal_identifier: crm_record[:id],
      internal_status: crm_record[:status],
      internal_status_at: crm_record[:sold_at],
      notes: crm_record[:notes],
      sale_sold_at: crm_record[:sold_at],
      sale_sold_by: crm_record[:sold_by],
      sale_currency: crm_record[:currency],
      sale_revenue: crm_record[:revenue],
      sale_lead_cost: crm_record[:cpl],
      sale_costs: crm_record[:costs],
      sale_gross_profit: crm_record[:profit],
    }.to_json,
    headers: { 'Content-Type' => 'application/json' }
  )
end

# Example CRM/database record
# Remember to encode the datetimes as ISO8601!
# In the example below the dates contain British Summer Time zone info
crm_record = {
  connect_id: 'eb908232-4a92-4de0-95df-f511aa8d9f8b',
  cert_url: 'https://cert.contactstate.com/certs/ac5e67f3-d280-4209-a2d6-06474d3eae23',
  id: 'QWERTY1',
  received: '2020-01-10T15:15:30+0100',
  status: 'Sale',
  sold_at: '2020-01-15T12:00:45+0100',
  sold_by: 'John S',
  notes: 'Lovely stuff',
  currency: 'GBP',
  revenue: '1000.90',
  cpl: '35.15',
  costs: '20.99',
  profit: '944.76'
}

# Call the update
# This would be triggered from a CRM 'create' or 'update' hook
response = update_contact_state(crm_record)
<?php

# Example function to map an internal CRM status to
# a status that Contact State will accept.
function internal_status_to_contact_state_status($internal_status) {  
  $lookup = array(
    'Unassigned' => 'NEW',
    'With advisor' => 'TRANSFER',
    'Quoted' => 'QUOTE',
    'Sale' => 'SOLD',
    'Wrong product' => 'INELIGIBLE',
    'Not Interested' => 'NOT_INTERESTED',
    'Cancelled' => 'CANCELLED',
    'Could not contact' => 'NO_CONTACT',
    'Invalid' => 'INVALID',
    'Wrong phone' => 'INVALID_PHONE',
    'Wrong email' => 'INVALID_EMAIL',
    'Hoax' => 'INVALID_HOAX',
    'Dupe' => 'DUPLICATE',
    'Test' => 'TEST'
  );

  if(isset($lookup[$internal_status])) {
    return $lookup[$internal_status];
  } else {
    return 'NEW'; # default to 'NEW'
  }
}

# HTTP POST call to push data to Contact State
function update_contact_state($crm_record) {
  $secret_key = 'REPLACE_ME_WITH_YOUR_SECRET_KEY';

  $content = json_encode(array(
    'connect_id' => $crm_record['connect_id'],
    'secret_key' => $secret_key,
    'status' => internal_status_to_contact_state_status($crm_record['status']),
    'status_at' => $crm_record['sold_at'],
    'status_by' => $crm_record['sold_by'],
    'received_at' => $crm_record['received'],
    'internal_identifier' => $crm_record['id'],
    'internal_status' => $crm_record['status'],
    'internal_status_at' => $crm_record['sold_at'],
    'notes' => $crm_record['notes'],
    'sale_sold_at' => $crm_record['sold_at'],
    'sale_sold_by' => $crm_record['sold_by'],
    'sale_currency' => $crm_record['currency'],
    'sale_revenue' => $crm_record['revenue'],
    'sale_lead_cost' => $crm_record['cpl'],
    'sale_costs' => $crm_record['costs'],
    'sale_gross_profit' => $crm_record['profit']
  ));

  $curl = curl_init('https://perform.contactstate.com/leads');
  curl_setopt($curl, CURLOPT_HEADER, false);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
  curl_setopt($curl, CURLOPT_POST, true);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $content);

  # Make a POST request to claim the Certificate
  $json_response = curl_exec($curl);
  $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

  if ($status != 201) {
    die("Error: call to URL {$crm_record['cert_url']} failed");
  }
  curl_close($curl);

  # Get the Certificate URL from the response
  # This should be stored and passed along to the Data Buyer
  $response = json_decode($json_response, true);
  return $response;
}

# Example CRM/database record
# Remember to encode the datetimes as ISO8601!
# In the example below the dates contain British Summer Time zone info
$crm_record = array(
  'cert_url' => 'https://cert.contactstate.com/certs/ac5e67f3-d280-4209-a2d6-06474d3eae23',
  'connect_id' => 'eb908232-4a92-4de0-95df-f511aa8d9f8b',
  'id' => 'QWERTY1',
  'received' => '2020-01-10T15:15:30Z+0100',
  'status' => 'Sale',
  'sold_at' => '2020-01-15T12:00:45+0100',
  'sold_by' => 'John S',
  'notes' => 'Lovely stuff',
  'currency' => 'GBP',
  'revenue' => '1000.90',
  'cpl' => '35.15',
  'costs' => '20.99',
  'profit' => '944.76'
);

$response = update_contact_state($crm_record);

?>

It's possible to update the Certificate with the following fields. Please note, the only required filed is 'Status', the rest are optional. This API should be called every time the status changes, or there is new information to send.

Field
Description
Connect ID
connect_id (String uuid)
The Connect ID to associate the sales data with.
Certificate URL
certificate_url (String)
The Contact State certificate URL to associate the sales data with.
Status
status (String)
The Contact State sales status. Please find a list of available statuses in the appendix.
Status at
status_at (ISO 8601 timestamp)
The time that the Contact State sales status changed.
Status by
status_by (String)
A string identifying the person that caused the status to change (e.g. the sales advisor).
Received at
received_at (ISO 8601 timestamp)
The time the consumer data was received by the CRM.
Internal identifier
internal_identifier (String)
The unique internal CRM ID. We recommend this is a numeric ID and not anything personally identifiable, like an email address.
Internal status
internal_status (String)
The CRM status as it exists in the Buyer's CRM. This is used for debugging status related issues.
Internal status at
internal_status_at (ISO 8601 timestamp)
The time that the CRM status in the Buyer's CRM system changed. This is used for debugging status related issues.
Notes
notes (String)
Any notes concerning the consumer data.
Sale sold at
sale_sold_at (ISO 8601 timestamp)
The time the sale was made.
Sale sold by
sale_sold_by (String)
A string identifying the person that made the sale.
Sale currency
sale_currency (ISO 4217 currency code)
Currency the sale happened in. Defaults to GBP.
Sale revenue
sale_revenue (Decimal as string)
Revenue attributed to the sale. e.g. '99.99'
Sale lead cost
sale_lead_cost (Decimal as string)
CPL for this sale. e.g. '30.00'
Sale costs
sale_costs (Decimal as string)
Any other costs associated with this lead. e.g. '0.99'
Sale gross profit
sale_gross_profit (Decimal as string)
Gross profit for this lead. Defaults to revenue - costs but can be explicitly set in this field.

Sending a push

A POST request needs to be made to the Perform endpoint:

POST https://perform.contactstate.com/leads

with a body that conatins a secret key and then a list of fields to be updated.

On success the endpoint will return an HTTP 201.

Error handling

Errors will be returned with a HTTP status code of 500 and the following JSON:

{ "error": { "message": "Error message" } }

CRM triggers

Contact State can easily work with the majority of CRM systems on the market.

Most CRM systems provide a mechanism to subscribe or hook into CRM lifecycle events (e.g. create, update) and trigger an action. On each of these events a call to Contact State can be made, sending along an updated lead status.

We recommend sending at least two updates:

  1. On create: When the lead is first received the Internal ID, Received At time and initial Status are sent.
  2. On update: When the Status changes both the Status and Notes fields get updated too.

If you have a custom in-house CRM, you will need to get your development team to create this functionality. If you're using an off the shelf solution then please consult their documentation.

Pulling Sales Data

Data Sellers can pull feedback data from Contact State via a single API call.

This data is also available in the Contact State platform and downloadable as a spreadsheet.

To pull sales data you will need

  1. A data sharing agreement with the Buyer. The Buyer needs to agree to share this data with you and turn it on in the platform.

Authentication

Authentication is required for all calls to Perform.

The authentication mechanism is an API key. This can be found within your Contact State account. This is the same method and Bearer token as used in the Connect calls.

The API key is set as an HTTP 'Authorization' header with all requests.

Authorization: Bearer your-api-key-goes-here

GET /stats/:id(/:delivery_id) - Retrieving stats about a single lead

Sales stats about a lead can be retrieved by making an API request that looks like this:

GET https://perform.contactstate.com/stats/:id(/:delivery_id)

Where :id is the Contact State Connect ID of the lead (this will be returned in the create request response payload).

Optionally, a delivery ID (delivery_id) can also be passed in the URL which is useful for differentiation if you have delivered the same lead to multiple buyers. If the delivery ID is left blank then the last successful delivery for the lead stats will be returned.

Response

If successful, the endpoint will respond with HTTP code 200 and with all the returned stats fields.

Returned stats fields

Field
Description
Status
status (String)
The Contact State sales status. Please find a list of available statuses in the appendix.
Status at
status_at (ISO 8601 timestamp)
The time that the Contact State sales status changed.
Sale sold at
sale_sold_at (ISO 8601 timestamp)
If enabled by the buyer, the time the sale was made.
Sale currency
sale_currency (ISO 4217 currency code)
If enabled by the buyer, currency the sale happened in. Defaults to GBP.
Sale revenue
sale_revenue (Decimal as string)
If enabled by the buyer, revenue attributed to the sale. e.g. '99.99'
Sale lead cost
sale_lead_cost (Decimal as string)
If enabled by the buyer, CPL for this sale. e.g. '30.00'
Sale costs
sale_costs (Decimal as string)
If enabled by the buyer, any other costs associated with this lead. e.g. '0.99'
Sale gross profit
sale_gross_profit (Decimal as string)
If enabled by the buyer, gross profit for this lead.

POST /stats - Bulk data export

A spreadsheet of feedback data is available through the API by making a post request to /stats.

POST https://perform.contactstate.com/stats

This will enqueue a spreadsheet generation job and will respond with a job URL.

{ "job_url": "https://perform.contactstate.com/jobs/7481cfc5-43c8-4148-8cea-8d6fdd319723" }

You can see the status of the spreadsheet generation by making a GET request to the job URL.

GET https://perform.contactstate.com/jobs/7481cfc5-43c8-4148-8cea-8d6fdd319723

The response looks like:

{ "status": "pending", "download_url": null }

Ping the endpoint every 10 seconds or so until the status is "success". Jobs should complete within 30 - 90 seconds.

A successful response looks like:

{ "status": "success", "download_url": "https://contactstate-job-downloads.s3.eu-west-2.amazonaws.com/downloads/..." }

The spreadsheet can be downloaded by following download_url. The link will stay valid for 7 days.

Real time webhook

Data Sellers can receive a webhook from Contact State as soon as Contact State receives data from the buyer.

The webhook URL can be configured in the platform under Distribution settings.

The webhook URL you specify will receive a post request with the perform fields.

Field
Description
Status
status (String)
The Contact State sales status. Please find a list of available statuses in the appendix.
Status at
status_at (ISO 8601 timestamp)
The time that the Contact State sales status changed.
Sale sold at
sale_sold_at (ISO 8601 timestamp)
If enabled by the buyer, the time the sale was made.
Sale currency
sale_currency (ISO 4217 currency code)
If enabled by the buyer, currency the sale happened in. Defaults to GBP.
Sale revenue
sale_revenue (Decimal as string)
If enabled by the buyer, revenue attributed to the sale. e.g. '99.99'
Sale lead cost
sale_lead_cost (Decimal as string)
If enabled by the buyer, CPL for this sale. e.g. '30.00'
Sale costs
sale_costs (Decimal as string)
If enabled by the buyer, any other costs associated with this lead. e.g. '0.99'
Sale gross profit
sale_gross_profit (Decimal as string)
If enabled by the buyer, gross profit for this lead.
Notes
notes (Stringg)
If enabled by the buyer, notes for this lead.

Support

Please email support@contactstate.com for integration support.

Appendix

Certificates

Contact State believes that all data transfer in the future will have an accompanying Certificate that records where that data came from and documents the environment it was generated in.

The output of all Contact State integrations is a Certificate. Certificates look like this

An example Contact State Certificate

and are accessible through URLs that look like this

https://cert.contactstate.com/certs/534ede1d-9430-4bc9-8aa5-6626d2ab5a12

Certificates are designed to be passed from Data Seller to Data Buyer and serve as a record of a particular consumer transaction.

Data privacy

Contact State is designed with privacy and security by default.

All data that is sent to us is hashed with the SHA-256 Cryptographic Hash Algorithm.

In a Certify integration the data is hashed client side in a browser before it is sent to our servers. We never see plain text customer data and all data transfer is secure and happens over TLS 1.2.

A hashing algorithm is a deterministic, one way transformation of data that always produces the same output for a given input. For example, the string "hello" produces the SHA-256 hash of 2cf24dba5fb0a30e26e83b2ac 5b9e29e1b161e5c1fa7425e73 043362938b9824

Contact State is unable to see the original input. Instead, Contact State looks for patterns in the data to power features on the platform.

CRM Sales Statuses

The lead statuses that can be sent to Contact State are:

State Description
NEW New lead
CONTACTED The lead has been contacted
TRANSFER Lead is with an advisor
QUOTE The lead has been quoted
SOLD The lead has been sold to
INELIGIBLE The lead is outside of product criteria
NOT_INTERESTED The lead was not interested
CANCELLED The lead cancelled
NO_CONTACT The lead was uncontactable
INVALID The lead data was invalid
INVALID_PHONE The phone number was invalid
INVALID_EMAIL The email was invalid
INVALID_HOAX The lead was a hoax
DUPLICATE The lead is a duplicate
TEST The lead was a test