AWS Lambda Python function fails when sending JSON payload to HubSpot CRM API

I’m having trouble with my AWS Lambda function when trying to send customer data to HubSpot through their API. The same data works perfectly when I test it manually with Postman, but when my Python code runs it gives me an error.

Working example from Postman:

import requests
import json

# This works fine when testing manually
api_endpoint = "https://api.hubapi.com/crm/v3/objects/contacts"
api_key = "your_key_here"

customer_data = {
    "properties": {
        "api_source": "yes",
        "email_address": "[email protected]",
        "company_name": "Tech Solutions LLC",
        "street_address": "123 Main Street",
        "city_name": "Chicago",
        "state_code": "IL",
        "postal_code": "60601",
        "quote_date": "2021-11-15",
        "service_start": "2022-02-01",
        "utility_company": "ComEd",
        "service_type": "electricity",
        "rate_option_1": 5.25,
        "rate_option_2": 4.89,
        "rate_option_3": 4.65,
        "contract_12": 12,
        "contract_24": 24,
        "contract_36": 36,
        "sales_rep": "John Smith",
        "rep_phone": "555-123-4567",
        "rep_email": "[email protected]"
    }
}

The error I get:

Invalid input JSON: Cannot build ObjectSchemaEgg, Invalid input JSON on line 1, column 2: Cannot build ObjectSchemaEgg, some of required attributes are not set [name, labels]

My Lambda function that’s not working:

def send_to_hubspot_api(contact_info):
    print('Starting HubSpot API call')
    json_string = json.dumps(contact_info)
    request_payload = {"properties": json_string}
    print('Payload ready:', request_payload)
    
    api_response = requests.request(
        "POST", 
        hubspot_endpoint + api_token, 
        headers={'Content-Type': 'application/json'}, 
        data=request_payload
    )
    response_data = json.loads(api_response.text)
    print('API Response:', response_data)

def process_customer_file(file_event, s3_object):
    try:
        csv_data = pd.read_csv(s3_object['Body'], encoding="ISO-8859-1")
        records = csv_data.replace(np.nan, 'null', regex=True).to_dict(orient='records')
        
        for record in records:
            customer_obj = {}
            
            if 'api_source' in record and record['api_source'] != 'null':
                customer_obj['api_source'] = record['api_source']
            
            if 'company_name' in record and record['company_name'] != 'null':
                customer_obj['company_name'] = record['company_name']
                
            if 'street_address' in record and record['street_address'] != 'null':
                customer_obj['street_address'] = record['street_address']
                
            # ... more field processing ...
            
            send_to_hubspot_api(customer_obj)
            
    except Exception as error:
        print(f"Processing failed: {error}")
        return False

The dictionary structure looks correct to me but HubSpot keeps rejecting it. I think there might be an issue with how I’m formatting the JSON payload in my Lambda function compared to the working Postman request. Can anyone spot what I’m doing wrong here?

you’re double-encoding your json. you’re doing json.dumps(contact_info) then wrapping it in another dict, which breaks everything. change request_payload = {"properties": json_string} to request_payload = {"properties": contact_info} and use json=request_payload instead of data=request_payload in your request.

Your payload construction is the problem. You’re using json.dumps(contact_info) and then wrapping that in another dictionary - this creates malformed JSON. HubSpot wants actual key-value pairs in the properties object, not a JSON string.

Ditch the json.dumps() call completely and pass contact_info directly:

request_payload = {"properties": contact_info}

Also change data= to json= in your requests call. The json= parameter handles serialization automatically and sets the right headers. I hit this exact same issue with HubSpot last year - spent hours debugging before I figured out the double-encoding was breaking everything. Your Postman example works because you’re passing the raw dictionary structure.