Connecting a HubSpot integration

Your integration keeps calling HubSpot’s legacy v1–v3 endpoints. You change the base URL and the credential; the bridge performs each call through HubSpot’s date-based API with a token of your portal and answers in the legacy format: the same fields, paging and error bodies.

1. Licence key, console or agency panel

Buy a plan on the pricing page: the licence key (FRG-HUBS-…) arrives by e-mail a few minutes after payment. One portal: sign in to the console with the key and your e-mail; the first sign-in shows a bridge key (brg_hs_…) once. Agencies with several client portals sign in to the agency panel with a link sent by e-mail and add each portal with its licence key; every portal has its own bridge keys, token and counters.

2. HubSpot token and scopes

Create a private app (or a static-auth app) in the HubSpot portal and paste its access token into the console. It is stored sealed and never shown again; replacing it is one paste. Give the app the scopes of the APIs your integration calls:

Your integration usesScopesNote
Contacts (v1 contacts, CRM v3 contacts)crm.objects.contacts.read, crm.objects.contacts.writeNotes, calls, meetings and tasks (engagements) also need the scopes of the records they belong to.
Companies (v2 companies, CRM v3 companies)crm.objects.companies.read, crm.objects.companies.write
Deals (v1 deals, CRM v3 deals)crm.objects.deals.read, crm.objects.deals.write
Ticketscrm.objects.tickets.read, crm.objects.tickets.write
Ownerscrm.objects.owners.read
Properties (v1/v2 properties, CRM v3 properties)crm.schemas.contacts.read / .write, crm.schemas.companies.read / .write, crm.schemas.deals.read / .writePer object type you use.
Custom objectscrm.objects.custom.read / .write, crm.schemas.custom.read
Listscrm.lists.read, crm.lists.write
One-to-one e-mail engagementssales-email-readOnly if you read e-mail engagements.
Forms (v2 forms)formsForm submissions to api.hsforms.com need no token.
Uploaded form filesforms-uploaded-files
Files (file manager)files (or files.read / files.write / files.delete)
Importscrm.import
E-mail subscriptions (email/public/v1/subscriptions)communication_preferences.read_write (communication_preferences.read to read only)Needed even if your token worked with marketing e-mail scopes: the bridge calls the date-based subscriptions API, which asks for these.
Blog (CMS v2 blog posts, v3 blog topics and authors)content
Transactional e-mailtransactional-email
Workflows (automation v2–v4) and workflow actions (Actions V4)automation
Line items, products, quotes, orders, cartscrm.objects.line_items.read / .write, crm.objects.quotes.read / .write, crm.objects.orders.read / .write, crm.objects.carts.read / .writePer object type you use.

Scope names as published by HubSpot. When HubSpot answers 403 for missing scopes, the legacy error comes back unchanged and the gap report shows the operation.

3. Point the integration at the bridge

Replace the base URL https://api.hubapi.com with https://hubspot.avakode.com and keep every path as it is (https://hubspot.avakode.com/contacts/v1/lists/all/contacts/all). Many integrations keep the base URL in configuration or in one constant of their HTTP layer.

If the URL is hard-coded, change it in code or in the configuration of the HTTP layer the integration uses. A hosts-file entry that sends api.hubapi.com to the bridge does not work: TLS certificates for api.hubapi.com belong to HubSpot, so the client rejects the connection.

4. Token emulation

Where the integration sent a private app token (Authorization: Bearer pat-…), send Authorization: Bearer <bridge key>. Where it sent a legacy API key (?hapikey=…), send ?hapikey=<bridge key> — the bridge removes it before calling HubSpot. Prefer the header: keys in URLs end up in proxy logs.

What is translated

1,067 of 1,293 legacy operations are translated to the date-based API today (996 fully, 71 partly); 60 more, with no date-based version, are passed through to HubSpot’s legacy endpoint while HubSpot keeps it. The coverage table is generated from the bridge’s registry — the list the bridge dispatches on. Translated calls go to the date-based endpoint shown; partial ones say what is not carried; calls not translated answer 501 BRIDGE_UNSUPPORTED and appear in the gap report — never an empty success.

Paging and cursors

Legacy offset / vid-offset values keep working: for object lists they are the same numbers as the date-based after cursor, so they pass through unchanged. When HubSpot returns an opaque cursor, the bridge hands your integration a numeric offset above 9·1015 that stays valid for 24 hours; an expired one answers 400 VALIDATION_ERROR “Unknown or expired offset” — restart from the first page. paging.next.link and Location headers point back to the bridge.

Errors

AnswerMeaningWhat to do
501 BRIDGE_UNSUPPORTED “Not translated by Avakode HubSpot Bridge: …”The operation is not translated (see the table).Check the gap report; tell us which operations matter to you.
429 RATE_LIMIT with Retry-After and policyNameHubSpot’s own limit (its answer passes through); or the bridge holding calls back to keep the portal within HubSpot’s limits — 100 HubSpot calls per 10 seconds until HubSpot’s X-HubSpot-RateLimit-* headers show the portal’s own limit, five searches per second (on the hosted bridge counted per worker instance; HubSpot’s own 429 with Retry-After remains the backstop); a call waits up to 12 seconds before this answer, and the X-Avakode-Bridge-Limit header says which limit it was; or the bridge’s memory for large answers is in use (bridge memory, retry after 2 seconds); or more than 60 requests per second per portal. One legacy call takes one or more HubSpot calls (a contact profile or a list page: two), so a portal gets fewer legacy calls through than HubSpot’s limit.Wait Retry-After seconds and retry.
504 BRIDGE_UPSTREAM_TIMEOUTHubSpot did not answer one call within 30 seconds. A call waiting for its turn under HubSpot’s limits waits up to 12 seconds first; if your integration closes the connection meanwhile, the bridge does not call HubSpot for it.Retry; keep your client’s timeout above 45 seconds.
401 INVALID_AUTHENTICATION “…bridge console…”HubSpot rejected the portal token (expired, revoked).Paste a new token in the console.
401 INVALID_AUTHENTICATION “Send your Avakode bridge key…”Missing, wrong or revoked bridge key.Use a current bridge key.
503 BRIDGE_NOT_CONFIGUREDNo HubSpot token saved for this portal yet.Paste the token in the console.
403 BRIDGE_SUSPENDEDThe licence is not active.Renew the licence; the bridge reactivates within a day.

Scanner

Before switching, find every legacy call in your code: upload a ZIP or paste URLs to the web scanner (nothing is stored), — you get each call, whether the bridge translates it and why not. A command-line version of the same engine for scanning large repositories on your own machine is available on request.

Agencies and white label

The agency panel manages many client portals under one e-mail sign-in, with per-portal keys, tokens, counters and a journal of untranslated calls. Serving the bridge under your own domain (a CNAME to the bridge) is available on request.

Privacy

The bridge stores: hashes of bridge keys, the portal token sealed with AES-GCM, daily counters, coverage events with operation, status, reason and latency, and, as short-lived metadata, opaque HubSpot paging cursors (24 h), property names and types (10 min), engagement types and the portal id (24 h). It does not store contacts, companies, deals, property values or files — they pass through memory only. Logs carry no request URLs, so a hapikey never lands there.

Self-hosted

The same bridge runs on your own server as a Docker image (licence plan selfhosted, no tenant limit, data in one volume). Ask us for the self-hosted guide.