Pull live contact data from HubSpot into a WhatsApp chatbot. When a lead messages you, look up their record, check lifecycle stage and deal status, and reply with personalised context, all without leaving WhatsApp.
Before following this guide, read the External API Request step foundation guide. It covers every field in the step interface so this guide can focus on HubSpot-specific values.
HubSpot removed the old hapikey-based API keys in 2023. All integrations now use Private App access tokens. If you have an older integration using hapikey= in the URL, it no longer works. This guide uses the current Private App method only.
A Private App is HubSpot's way of generating a scoped Bearer token for internal integrations. You configure exactly which permissions it has.
crm.objects.contacts.read. If you also need deals, add crm.objects.deals.read.pat-na1- or pat-eu1-. Copy it immediately.HubSpot displays the full Private App token only once after creation. A masked version is visible later but the full token cannot be retrieved. If lost, click Actions > Rotate token in the Private Apps settings to generate a new one.
HubSpot Private Apps: developers.hubspot.com/docs/api/private-apps
In your WA.Expert chatbot flow, collect the HubSpot contact ID from the customer and store it as {{contact_id}}. Then add an External API Request step:
| Field | Value | Notes |
|---|---|---|
| Select Method | GET | Reading an existing contact record. |
| Request URL | https://api.hubapi.com/crm/v3/objects/contacts/{{{contact_id}}}?properties=firstname,lastname,... | The ?properties= query string controls which fields are returned. Without it, only default properties come back. List each property name you need. |
| Select Auth Type | No Auth | The Bearer token goes in the Authorization header below. |
| Authorization | Bearer pat-na1-xxx... | Include the word 'Bearer' followed by a space, then your Private App token. EU accounts use pat-eu1-. |
| Content-Type | application/json | Standard header for HubSpot API requests. |
| Select Body Type | None | GET requests carry no body. |
| Choose Response Type | JSON | HubSpot returns structured JSON. |
Without a ?properties= query parameter, HubSpot returns a minimal default set. Add the property internal names you need. For example: firstname, lastname, email, phone, lifecyclestage, hs_lead_status, company, hubspot_owner_id. Find property internal names in HubSpot Settings > Properties.
A successful HubSpot contact response looks like this:
{
"id": "12345",
"properties": {
"firstname": "Priya",
"lastname": "Sharma",
"email": "priya@example.com",
"phone": "+919820000001",
"company": "Sharma Textiles",
"lifecyclestage": "lead",
"hs_lead_status": "IN_PROGRESS",
"createdate": "2026-06-15T10:30:00.000Z",
"lastmodifieddate": "2026-06-20T14:00:00.000Z"
},
"createdAt": "2026-06-15T10:30:00.000Z",
"updatedAt": "2026-06-20T14:00:00.000Z",
"archived": false
}HubSpot wraps every contact's data inside a 'properties' object. Map your variables using properties.fieldname notation: properties.firstname, properties.lifecyclestage, etc. Only id, createdAt, and updatedAt are at the top level.
Map these response paths to variables in the External API Request step:
| Variable name | Response path | Example value |
|---|---|---|
| contact_id_hs | id | 12345 |
| first_name | properties.firstname | Priya |
| last_name | properties.lastname | Sharma |
| properties.email | priya@example.com | |
| company | properties.company | Sharma Textiles |
| lifecycle_stage | properties.lifecyclestage | lead |
| lead_status | properties.hs_lead_status | IN_PROGRESS |
Lifecycle stage values: subscriber, lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, other. Lead status values: NEW, OPEN, IN_PROGRESS, OPEN_DEAL, UNQUALIFIED, ATTEMPTED_TO_CONTACT, CONNECTED, BAD_TIMING.
If the contact messaged you first within the last 24 hours, your reply is a free service conversation. If initiating outbound follow-up from HubSpot data, use an approved Utility or Marketing template.
Contacts rarely know their HubSpot ID. Use the search endpoint to look them up by phone number or email. This is a POST request with a JSON body:
POST https://api.hubapi.com/crm/v3/objects/contacts/search
Authorization: Bearer pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Content-Type: application/json
Body:
{
"filterGroups": [{
"filters": [{
"propertyName": "phone",
"operator": "EQ",
"value": "+919820000001"
}]
}],
"properties": ["firstname", "lastname", "lifecyclestage", "hs_lead_status"]
}
Response:
{
"results": [{
"id": "12345",
"properties": {
"firstname": "Priya",
"lifecyclestage": "lead",
"hs_lead_status": "IN_PROGRESS"
}
}],
"total": 1
}
Map: results[0].properties.firstname, results[0].properties.lifecyclestageCustomer sends their phone number via WhatsApp.
WA.Expert stores it as {{customer_phone}}.
External API Request step (POST search):
POST https://api.hubapi.com/crm/v3/objects/contacts/search
Body: { "filterGroups": [{"filters": [{"propertyName":"phone","operator":"EQ","value":"{{customer_phone}}"}]}],
"properties": ["firstname","lifecyclestage","hs_lead_status","company"] }
Response mapped:
first_name = "Priya"
lifecycle_stage = "lead"
lead_status = "IN_PROGRESS"
company = "Sharma Textiles"
Bot replies:
"Hi Priya, your current status with us:
Account: Sharma Textiles
Stage: Lead
Status: In Progress
Your account manager will follow up shortly.
Reply URGENT if you need immediate attention."| Symptom | Likely cause | Fix |
|---|---|---|
| 401 Unauthorized | Invalid token or missing Bearer prefix | Check the Authorization header reads exactly 'Bearer pat-na1-YOUR_TOKEN'. Rotate the token in HubSpot Private Apps settings if needed. |
| 403 Forbidden | Token lacks required scope | Edit the Private App in HubSpot (Settings > Private Apps), add crm.objects.contacts.read scope, then rotate the token to get a new one with updated permissions. |
| 404 Not Found | Contact ID does not exist | Confirm the contact ID is correct. Use the search endpoint to find the contact first if the ID is uncertain. |
| Properties missing from response | Fields not requested in ?properties= query | Add the property internal names to the ?properties= parameter in the URL. Without this, HubSpot returns a minimal default set. |
| Private Apps option missing in Settings | Account does not support Private Apps | Some legacy free HubSpot accounts cannot create Private Apps. Upgrading to Starter or using OAuth is the alternative. |
| 429 Too Many Requests | Rate limit hit | HubSpot allows 100-400 requests/second depending on plan. Add retry logic with a delay. For the WA.Expert External API Request step, avoid calling HubSpot inside a loop. |
Free trial, no credit card required. And if you ever get stuck, we are the only platform in India that answers you live on WhatsApp.