HubSpot retired their classic API keys in 2022. The modern way to grant a third-party app access to your HubSpot data is a Private App — you create the app inside your own portal, tick exactly which scopes it may use, and HubSpot issues a long-lived access token to paste into Anexus.
HubSpot organises your data into four CRM objects — Contacts, Companies, Deals and Tickets. This integration asks for Contact scopes only, so the token it hands us physically cannot read or write your Deals or Tickets.
Who can create the token, and in which portal
- Private Apps are a Super Admin feature in HubSpot. A standard user — even one who can edit every contact — will not see the menu. If you are not a Super Admin, ask whoever owns the portal to do Step 1 and send you the token.
- Works on every HubSpot subscription, including the free CRM. Private Apps are not a paid feature.
- A token is bound to the single portal that created it. It cannot be moved to another HubSpot account, and a token made in a sandbox will only ever write to that sandbox.
Check your Hub ID first
If you have ever been added to a client portal, a partner portal or a sandbox, you probably have more than one HubSpot account. Your Hub ID is the number in the browser address bar — app.hubspot.com/contacts/12345678/… — and it is also shown under the account menu at the top right. Note it down before you start; if the contacts later land "nowhere", they are almost always sitting happily in a different Hub ID.
Create the Private App and copy its access token
In HubSpot, click the gear icon (top right) to open Settings, then go to Integrations → Private Apps.
Click Create a private app.
On the Basic Info tab:
- Name: Anexus Connect
- Logo: optional
- Description: "Auto-syncs contacts captured by Anexus to HubSpot"
Switch to the Scopes tab. The list is long, so use the search box at the top and tick exactly these two under CRM:
crm.objects.contacts.read— lets us look up an existing Contact by email before writing, so the same person is never duplicatedcrm.objects.contacts.write— lets us create the Contact and update its properties
Do not tick the Companies, Deals or Tickets scopes — they are not used, and a broader token is a bigger blast radius if it ever leaks.
Click Create app at the top right and confirm the dialog HubSpot shows.
HubSpot opens the app on its Auth tab and shows the Access token behind a "Show token" link. Reveal it, click Copy, and keep the tab open until Anexus confirms the connection.
What the token prefix tells you
A HubSpot private app token starts with pat- followed by the region your portal is hosted in — pat-na1-… for North America, pat-eu1-… for the EU data centre — then a long identifier. If what you pasted has no pat- prefix, you have copied the wrong string: the app ID and the client secret sit on the same screen and are easy to grab by mistake.
Connect it in Anexus
In Anexus, go to Settings → CRM Integrations.
Find the HubSpot card and click Connect.
Paste the access token. Click Save & connect.
Anexus runs a quick test call to HubSpot to confirm the token works and to retrieve your account name (so we can show it on the card). On success the card turns green — check that the account name shown is the portal whose Hub ID you noted earlier.
How an Anexus contact maps to HubSpot properties
Every contact captured via your exchange form becomes — or updates — a record on HubSpot's Contact object. HubSpot gives each property a human label and a lowercase internal name; the internal name is what you search for when you build a list or a workflow filter.
| Anexus field | HubSpot property | Internal name | Worth knowing |
|---|---|---|---|
email | HubSpot's deduplication key for the Contact object. No email, no sync. | ||
| First name | First name | firstname | Default HubSpot property, present in every portal. |
| Last name | Last name | lastname | Default HubSpot property, present in every portal. |
| Phone | Phone number | phone | Not the same property as Mobile phone number (mobilephone). |
| Company | Company name | company | A text property on the Contact. It is not the associated Company record — see below. |
| Job title | Job title | jobtitle | Free-text property, so anything typed on the exchange form lands intact. |
| (set by Anexus) | Lead status = New | hs_lead_status | Written as the internal value NEW. This is a dropdown property — see the validation error below. |
Email is the pivot of the whole flow. Because HubSpot deduplicates Contacts on the email address, we look the person up before writing: an existing record is updated in place, a new one is created. Someone who taps your card twice with the same address will never become two Contacts.
The exchange-form message and the note recording how the contact was captured (NFC tap, QR scan, Apple Wallet, shared link) travel with every contact as well — see Connecting a CRM for the complete list of what Anexus sends.
Company name vs. the Company object
This is the single most common source of "the company is wrong" tickets on HubSpot. company is a plain text property sitting on the Contact. It is a different thing from the Company object — the separate record with its own domain, industry and associated contacts.
HubSpot has a portal-wide setting, Settings → Objects → Companies → "Automatically create and associate companies with contacts". When it is on, HubSpot ignores the typed company name and instead builds the association from the email domain. So a lead who types "Acme Manufacturing" but hands over a @acme-mfg.ca address gets associated with whatever Company HubSpot already holds for that domain — and a lead using a free provider such as Gmail or Outlook gets no Company at all, because HubSpot deliberately excludes free email domains.
Nothing is broken when this happens — the name they typed is still on the Contact in the company property. If you want the two to agree, either turn the auto-association setting off, or build your views on company rather than on the associated Company.
Lead status, Lifecycle stage, and marketing contacts
New contacts arrive with hs_lead_status set to NEW, so they drop straight into the standard "New" Lead status filter and you can work the queue from the top.
Lead status is not Lifecycle stage. HubSpot ships two similar-sounding dropdowns: hs_lead_status (New, Open, In progress, Unqualified…) is the one this integration writes, while lifecyclestage (Subscriber, Lead, MQL, SQL, Customer…) is the funnel property most dashboards and lists are built on. If your saved view filters on Lifecycle stage, freshly synced people can be present in HubSpot and still invisible in that view. Worth remembering too: HubSpot will not move a Lifecycle stage backwards on its own, so a person who is already a Customer stays a Customer even when they tap your card as a fresh lead.
One more HubSpot-only wrinkle: if your portal uses Marketing Hub's marketing contacts model, contacts created through the API are normally created as non-marketing contacts. They are fully visible and fully workable by sales, but you cannot include them in a marketing email until they are switched to marketing contacts — by hand, or with a workflow that does it for you.
Backfilling the contacts you already captured
To backfill contacts captured before connecting, click Sync past contacts on the HubSpot card. We push them in batches of 50 with a live progress bar.
HubSpot enforces two request limits, and they work differently. The burst limit is counted per app over a rolling ten seconds — 100 requests on Free and Starter, 190 on Professional and Enterprise — so it is this integration's own traffic that counts against it, not everything else installed on your portal. The daily limit is the one shared across every app in the account, and its size depends on your subscription. If a large sync runs into either limit, HubSpot answers with a 429 and the contacts it refused stay pending; click Sync past contacts again a little later to push them.
Troubleshooting the errors HubSpot actually returns
When a sync fails, the HubSpot card shows the reason HubSpot gave us. These are the ones you are likely to see.
401 — authentication credentials not found
HubSpot does not recognise the token at all. Either it was truncated on the way over (check it still begins pat- and was not clipped by a password manager), or the private app has been deleted or rotated in HubSpot. Open Settings → Integrations → Private Apps, confirm "Anexus Connect" is still listed, re-copy the token from the Auth tab, and reconnect in Anexus.
403 — the app has not been granted the required scopes
The token is valid but its scope list is missing crm.objects.contacts.read or crm.objects.contacts.write. Scopes belong to the app, not to the token, so you fix this in HubSpot rather than in Anexus: open the Anexus Connect private app, go to the Scopes tab, tick the missing scope and commit the change. Then re-copy the access token from the Auth tab and reconnect. Ticking read and forgetting write is the usual mistake.
409 CONFLICT — "Contact already exists"
HubSpot refuses to create a second Contact with an email address it already holds, and helpfully returns the existing record's ID in the message. Because we look the person up first, you should not normally see this. When it does appear it means the address was added to HubSpot by something else in the same moment — a form submission, an import, another integration — and the retry will simply update that record instead.
400 — "Property values were not valid"
Almost always hs_lead_status. Lead status is an enumeration, and HubSpot only accepts the internal values defined in its option list. Portals that have customised their lead pipeline sometimes delete or rename the NEW option — at which point every sync fails validation. Fix it in Settings → Properties: open Lead status, and make sure an option with the internal value NEW still exists. You can relabel it to anything you like; it is the internal value that has to match.
429 — the portal hit its rate limit
Not a configuration problem — HubSpot is telling us to slow down. The burst limit behind most 429s is counted per app, so this is normally our own traffic during a large sync rather than another integration crowding you out; the daily limit is the one your other apps share. The contact stays pending and the next Sync past contacts picks it up. If 429s keep appearing on ordinary day-to-day captures rather than during a backfill, contact support.
The sync succeeded but you cannot find the contact
Work through it in this order. Click Test on the HubSpot card to confirm the token is still live. Then check the account name on the card against the Hub ID you noted — a token made in a sandbox writes only to the sandbox. Then search the person by email from HubSpot's global search rather than scrolling a list: a saved view only shows the records that match its filters, so a new Contact can be in HubSpot and still be absent from the view you happen to be looking at.
Duplicate contacts appearing anyway
Two records for one human almost always means two email addresses — the work address on Monday, the personal one at the trade show on Friday. HubSpot treats those as two distinct Contacts by design, and so do we. On Professional and Enterprise subscriptions, HubSpot's Data Quality → Manage duplicates tool will suggest merges for records that look like the same person. On Free and Starter that tool is not available, but you can still merge two records by hand from the Actions menu on a contact record. Either way, merging is safe, and future syncs on either address land on the surviving record.
Turning the connection off
Click Disconnect on the HubSpot card. We immediately stop pushing new contacts. To also revoke the token from HubSpot's side — which you should do whenever the person who created it leaves — go back to Settings → Integrations → Private Apps in HubSpot, open the Anexus Connect app, and click Delete. Deleting the app kills its token instantly and permanently; there is no undo, and a new app means a new token to paste.
Contacts already written stay in HubSpot exactly as they are. Disconnecting is not a delete.