HOW TO use PHP to collect data from a CUSTOM form and send it into the KEAP API using CURL

I am putting this here for anyone who needs to do 1 simple thing:

  • COLLECT DATA FROM A CUSTOM FORM => PUSH IT INTO KEAP

Here is what that process looks like.

  1. First off - get a master “Service Account Key”
    (waste no time on the XML or OpenAuth nonsense)
  2. make a few calls to the GET api to map custom fields
    (since the IDs for those fields are not listed anywhere in Keap)
  3. Create a new user in keap, fill out EVERY field you can, including the custom ones
  4. Then make a call to the GET api for that test user
  5. “map out” all the IDs from the result, and use that to construct your CURL
  6. do weird stuff with array nesting so the json encode works
    (consider quitting your job to give sponge baths to cats)
  • Here is some CODE :clap:
  • you can COPY/PASTE :clap:
  • that SHOULD BE listed in the documentation :clap:
    and works in PHP 8+
/////////////////// GET SETUP :: simply asking the api for a GET endpoint so we can map stuff that 'should be' readily available in the GUI 

$KEAP_API_KEY = 'KeapAK-blaaaaaaaaaaaaa';
$KEAP_API_URL = 'https://api.infusionsoft.com/crm/rest/v1';
$KEAP_API_ACTION = '/contacts';
			
			$keap_user_id = 16749; // your test user id will go here
			$queryString = http_build_query(array(
					"optional_properties"=>'lead_source_id,custom_fields'
				      // these dont work: contactUtmId,keapSourceId,contactId,utmCampaign
					 )); 
			$url = $KEAP_API_URL.$KEAP_API_ACTION."/$keap_user_id/?$queryString";       
			$ch = curl_init($url);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
				'URI: '.$KEAP_API_URL.$KEAP_API_ACTION,
				'X-Keap-API-Key: '.$KEAP_API_KEY,
				'Content-Type: application/json',
			));                                                                                           

			$response = curl_exec($ch);                 
			curl_close($ch);  

			///////////// feedback 
			$response = json_decode($response, true);                                                         
			$result_output = print_r($response, true); 

echo "

----------------------------- API CALL result_output for: $keap_user_id

";
echo $result_output; 

exit();
// now, you will never need that chunk of code again
// unless, you add a custom field. lol. and need to remap the ID
////////////////////////////////////////////////////////////////////

Now that you know the IDs for custom fields: On to the gold.
All we want to do is catch a form submission data in POST and send it to Keap.


/////////////////// POST SETUP :: sending data into the final endpoint
//		PUT :: insert or update
//			https://developer.infusionsoft.com/docs/rest/#tag/Contact/operation/createOrUpdateContactUsingPUT
//			RETURN :: catch ID

$KEAP_API_KEY = 'KeapAK-blaaaaaaaaaaaaa';
$KEAP_API_URL = 'https://api.infusionsoft.com/crm/rest/v1';
$KEAP_API_ACTION = '/contacts';

			$data = array(
				'given_name'		=>	$first_name, 	// fname
				'family_name'		=>	$last_name,	// lname
				'opt_in_reason'		=>	'Customer opted-in through webform', //Only single opt-in is available through the API. 
					// see: https://integration.keap.com/t/api-and-marketing-opt-in/4012/7
					// https://integration.keap.com/t/contact-marketing-status-opt-in-reason-after-unsubscribe/82773
					// prevents abuse when adding a user
								
				'contact_type'	=>	"Customer",
				'owner_id'	=>	1, // put an owner here, your lead sales person
				// otherwise if NO OWNER is set then other stuff does not work later

				'lead_source_id'	=>	24, // match this up with your lead source
				// to get it, you have to click on "lead sources" and then look at the URL bar. 
					
				'source_type'		=>	'WEBFORM',
				'duplicate_option'	=>	'Email', //put this in here or contacts dont merge.
				
				'email_addresses'	=>	array(array( // ya, 2 arrays here.
					'email' => $email,
					'field' => 'EMAIL1'
				)),
				'phone_numbers'	=>	array(array( // ya, 2 more
					'extension' => '',
					'field' => 'PHONE1',
					'number' => $phone, 
					// better format the phone, lol. api FAILS if its not formed correctly
				)),

				'notes'				=>	$person_notes,
				// these are PERSON NOTES, but its not in the documentation
				
				'website'			=>	$website,
				// watch out for this BS
				// the API call FAILS if you dont have http/https protocol
				// you will LOSE YOUR LEAD if you don't check for the protocol
				
				
				// TAGS:  not work. 
				//https://integration.keap.com/t/cant-seem-to-add-any-tags-when-creating-a-new-contact/16217/8
				//'tag_ids'		=>	array( array(216), 	// Contact Us array(4111), 	// testing GA4 ), // array of integers
				// sorry. tags NOT WORK. the documentation is wrong.
				
				// also see: https://integration.keap.com/t/cannot-create-a-new-contact/40476
				
				/* meh... problems here too, feel free to try. lol
				'origin'			=>	array(array(
					'date' 			=> $date,
					'ip_address' 	=> $ip
				)),
				*/
				
					'custom_fields'	=>	array(
					
						// pay close attention to this bs: 
							// we have a custom field called "Previous Job"
							// it has an ID of 38
							// had to find that out with a GET call in step 1
							// because Keap made no attempt to identify fields in the GUI
							// so, if I want data in THAT field, here is how its done
						array(
							'content' => array('Trade Show'), // stuff that into another array
							'id' => 38 //lol. absurd. deserving of harsh criticism
							//Add notes here - PreviousJob :: Website | Colleague | Email | Trade Show | Other
						),
						
						/// same deal on everything below
						
						array(
							'content' => array($short_notes), // strings, wrapped in an array for some reason...
							'id' => 44 //notes :: text string
						),
						
						//////////////// We put our UTM VARS HERE
						// note how these are CUSTOM FIELDS
						// dont get tricked by THIS:: https://developer.infusionsoft.com/docs/rest/#tag/Contact/operation/insertUTMsUsingPOST
						// you can NOT get those values into the user by using "insertUTMsUsingPOST" - its impossible
						// that page does not even belong in the documetation IMHO.
						// want UTM vars?
						// do it like this - and get your own IDs for each as custom fields.
						array(
							'content' => array( $utm_campaign ),
							'id' => 57 //utm_campaign :: string
						),
					
						array(
							'content' => array( $utm_medium ),
							'id' => 61 //utm_medium :: string
						),
					
						array(
							'content' => array( $utm_source ),
							'id' => 59 //utm_source :: string
						),
					
						array(
							'content' => array( $utm_content ),
							'id' => 63 //utm_content :: string
						),
					
						array(
							'content' => array( $utm_term ),
							'id' => 65 //utm_term :: string
						),
					
						array(
							'content' => array( $utm_keyword ),
							'id' => 67 //utm_keyword :: string
						),
						
					), ### end custom fields
			
				
			);
			$data = json_encode($data, true);     
			//echo $data;
			$url = $KEAP_API_URL.$KEAP_API_ACTION.'?optional_properties=lead_source_id'; 
			// tack on lead_source_id to the url here, if you need to perform other actions later.
			// in our case, we JUST want the lead IN our crm.
			$ch = curl_init($url);

			curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT"); // for PUT calls
			curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // for sending data
			curl_setopt($ch, CURLOPT_POST, true); 

			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($ch, CURLOPT_HEADER, FALSE);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array(
				'URI: '.$KEAP_API_URL.$KEAP_API_ACTION,
				'X-Keap-API-Key: '.$KEAP_API_KEY,
				"Content-Type: application/json;charset=UTF-8",
				"Accept: application/json, */*",
			));                                                                                           

			$response = curl_exec($ch);                 
			curl_close($ch);  

			///////////// feedback 
			$response = json_decode($response, true);                                                         
			$result_output = print_r($response, true); 

			$RECORD_ID = !empty($response['id']) ? $response['id'] : false;

////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
echo "

----------------------------- API CALL result_output

";
echo $result_output; 

end useful information

Now, for some TLDR; complaining and whining
NO ONE needs or wants to download a colossally bloated PHP SDK to make these simple calls to get lead data from a website into => keap

Using custom landing pages?
Using WordPress?
Just hook into your contact form POST event and run this

Its as if the people who make these CRM systems (Salesforce, Sharpspring, HouseCallPro, even Oracle NetSuite) are completely unaware of the FRONT LINE of DSP advertising.

We need to TRACK Google Click IDs and UTM vars
it should be QUICK AND EASY to get data into our CRM systems.
The ambiguity of the documentation, human errors, lack of quality support in forums and no INDUSTRY STANDARDS on ALL of those “top tier” CRMs is awful. Wild West. Everyone just “making things up” by their own standards.

Some boo hoo:

  • I am disgusted by the bloated size of the PHP SDK
  • I was physically harmed (cortisol release and stress) by the poor documentation
  • deeply disturbed by the complete lack of professionalism and misinformation in the API forum (no answers, bad answers, misinformation, old information, people trying to promote their products in comments, brush offs, sarcasm)

Other than that?
It’s a pretty good system. lol.
Hope no one takes this personally.