Prerequisites
- A Text account with API access permission.
- A HubSpot account with permission to create and install an app.
- One or more customer properties already defined in Text or through the API.
- A matching custom contact property created in HubSpot for each Text customer property you want to sync. See HubSpot’s Create and edit properties guide.
- Docker with Docker Compose v2.
cloudflaredto expose the local service through a Cloudflare Quick Tunnel. You don’t need it if the service already has a public HTTPS address.- Git to clone the sample repository.
livechat/text-hubspot-contact-integration
Loading repository data...
Set up the integration
This guide runs a webhook service between Text and HubSpot. When a conversation closes, Text sends achat_deactivated webhook. The service retrieves the customer, their Text-generated summary, and any customer properties you’ve mapped. It then creates or updates the matching HubSpot contact by email, writes the mapped properties onto it, and adds the summary to its timeline.
1
Create the Text API access
In Text, go to Settings → API access → Personal access tokens. Create a personal access token with the following scopes:
chats--all:rw— to read chats and request summaries in any group, orchats--access:rwto limit access to groups that the token owner is a member of.customers:ro— to read the customer’s email and name, and customer properties.webhooks.configuration:rw— to register the webhook.
account_id:personal_access_token.Go to Settings → API access → OAuth clients, create an OAuth client for the integration, and copy its client ID. You will register the webhook for this OAuth client.The personal access token authorizes the Text API calls used by the service and webhook setup. The OAuth client ID identifies the integration that owns the registered webhook.2
Create the HubSpot API access
Follow HubSpot’s app creation guide to create and upload an app. Choose private distribution and static authentication, then configure these scopes:
crm.objects.contacts.readto find a contact by email.crm.objects.contacts.writeto create or update the contact data and its properties.crm.objects.notes.writeto create the associated note.
HUBSPOT_ACCESS_TOKEN when configuring the service.Static authentication connects the sample to one HubSpot account. Use OAuth instead when your app needs to connect multiple HubSpot accounts.The service uses the HubSpot contacts API for contact management and the HubSpot notes API to add summaries to contact timelines.3
Clone and configure the service
Clone the repository and enter its directory:Create a To sync Text customer properties to HubSpot, build a JSON object that pairs each Text customer property’s name with the internal name of its matching HubSpot contact property, then set it as Get each key from Text’s List customer property definitions response — its Set the generated value as The command prints a temporary
.env file with the following values:TEXT_TO_HUBSPOT_PROPERTY_MAP. Wrap the JSON in single quotes to avoid escaping the double quotes:name field is the property’s name as defined in Text.Get each value from HubSpot’s matching contact property — a HubSpot property has both an internal name and a display label, and the map needs the internal name, not the label. Find it in HubSpot under Settings → Properties, or read the name field from HubSpot’s Get all properties response (GET /crm/v3/properties/contacts).The service checks this map on every sync. Text properties that aren’t in the map are skipped, and each HubSpot property in the map must already exist. Leave the variable empty, or omit it, to sync only chat summaries.Generate a webhook secret with a command such as:TEXT_WEBHOOK_SECRET.The service listens locally at http://localhost:8080, but Text needs a public HTTPS address to deliver webhook events. For local testing, a free Cloudflare Quick Tunnel gives the local service a temporary public address.To start the tunnel, run cloudflared in a separate terminal:https://<random-name>.trycloudflare.com address. Keep the tunnel running while you test. If its address changes after you register the webhook, unregister the old webhook before registering it again.Set WEBHOOK_PUBLIC_URL to the tunnel address without a trailing slash. For a deployed service, skip the tunnel and use the service’s public HTTPS address instead.The repository excludes .env from Git. Keep the file private because it contains credentials for both systems.4
Register the Text webhook
Load the values from Register a license-level The response contains the webhook ID.Run the registration once. To check the registrations owned by a specific OAuth client ID, use List webhooks.
.env into your terminal session:chat_deactivated webhook with the register_webhook method:5
Start the service
Build and start the container:Confirm that the service is healthy:A healthy service returns HTTP
204.Follow its logs while you test the integration:6
Verify the HubSpot contact and note
The service matches HubSpot contacts by customer email and skips chats without one. It maps the Text customer name to HubSpot
firstname and lastname by splitting at the first whitespace.To test the service, start a chat as a customer with an email address, exchange at least one message, then close the chat.The service log should contain synced Text chat with the Text chat ID, thread ID, and HubSpot contact ID. In HubSpot, open the contact with the same email address. Its activity timeline should contain a note with the summary title and bullet points and the chat details. If you set TEXT_TO_HUBSPOT_PROPERTY_MAP, the mapped contact properties should also match the customer’s property values in Text.Use synced properties in HubSpot
In our example, you mapped Text’sCompany size property to HubSpot’s company_size. If a customer’s company size is set to Enterprise, this value syncs onto their HubSpot contact the moment their chat closes. From there, you can filter a segment on company_size to send a custom mailing campaign, or set a workflow enrollment trigger for every enterprise-tier contact to be routed into a different communication track than your regular outreach.
The same pattern works for any property you map, because Text customer properties are whatever your team defines, no matter what you’re tracking.
A property marking a commercial detail, like a plan or contract status, can drive a workflow the moment it changes. A property flagging risk, like a support escalation or usage drop, can route or prioritize a contact differently. A property used for segmentation, like team size or industry, can filter a list for a specific campaign. Whatever the property is, once it’s synced, it’s just data HubSpot can act on.
If none of this applies yet, there may be a distinction your team already makes in conversation — just not as a property yet. Treat the properties you map here as a starting point. Which ones are worth defining, and what HubSpot does with them, is up to your team.
Troubleshooting
Prepare the design for production
The sample demonstrates the integration flow, but it does not protect every sync from service restarts, temporary API failures, or repeated webhook deliveries. Before deploying the service, adapt the sample in three areas.Make sync delivery reliable
Make sync delivery reliable
The sample confirms receipt of a webhook before the sync finishes. It does not
save unfinished work or remember which threads it has processed, so a restart
can lose a sync and a repeated webhook can create a duplicate note.Before deploying:
- Save each sync job in a durable queue before confirming receipt of the webhook.
- Retry temporary Text and HubSpot failures, and alert your team when a job still fails.
- Record processed Text thread IDs so repeated webhook deliveries do not create another note.
- Continue delayed summary work after receiving the
thread_summary_setwebhook instead of relying on the sample’s 60-second polling window.
Define how HubSpot contacts are updated
Define how HubSpot contacts are updated
The sample matches contacts by email, updates name fields from Text, and creates
one note for every closed thread. Those choices may not match how your team
manages customer records in HubSpot.Before deploying:
- Decide whether Text or HubSpot owns each contact field.
- Store a Text customer ID to HubSpot contact ID mapping if email addresses can change.
- Decide how to handle customers without an email address or with duplicate HubSpot contacts.
- Limit
TEXT_TO_HUBSPOT_PROPERTY_MAPto the Text customer properties your HubSpot segments, campaigns, or workflows actually use. - Choose whether HubSpot receives one note per thread or one note per chat.
Protect customer data in both systems
Protect customer data in both systems
The sample exposes a webhook endpoint and copies customer identity and
conversation summaries from Text to HubSpot. It does not define how the copied
data is retained or deleted.Before deploying:
- Run the service behind a stable HTTPS endpoint with health checks and monitoring.
- Store credentials in a secrets manager and rotate the webhook secret.
- Copy only the customer data you need and restrict access to it.
- Define retention and deletion behavior for the copied data in both Text and HubSpot.