The platforms we build on

HubSpot

We make the CRM agree with itself.

You buy your own HubSpot licence, direct, at the price HubSpot publishes. We hold no partner tier and take nothing from what you pay them, so the advice below costs us the same whether you upgrade or stay where you are.

What follows is the specific: the objects and editions we work against, six things that only building against the API teaches, and the limits that decide how a job has to be shaped. Every one of them is checkable against your own portal. Every price we charge sits on one page.

What we actually touch.

  • The objects, and the edges between them

    Contacts, companies, deals, tickets, notes and the associations that join them. Associations are named by id rather than guessed — contact to company is 1, deal to contact is 3, deal to company is 5 — because the endpoint that takes an explicit type id succeeds where the convenience one refuses.

  • Every edition, and the difference between them

    Free, Starter, Professional and Enterprise. The gap that changes a build most is custom objects: they exist on Enterprise only, across every hub. A design that leans on a custom object is a design that quietly requires the top tier, and that is worth knowing on day one rather than in month three.

  • The current API, and the one before it

    HubSpot moved to date-based versions and issues a new one each March and September with an eighteen-month minimum support window. Both the date-based route and the older v3 answer live today. We probe rather than assume: documentation is a claim, a 200 is evidence.

  • Authentication as it stands today

    Service keys, not private app tokens. HubSpot’s own dialog says the legacy path will not receive new scopes or features and has limited support going forward. A plan written against a tutorial from last year is already pointing at the route being retired, which is the kind of thing worth checking before the first line is written.

  • Files and attachments, by record rather than by library

    Attachments are reached through the note that owns them, not by listing the file library. Listing the library would count marketing images and template assets that were never attached to a record, which inflates every total taken from it.

Six things that only building against it teaches.

Each of these produces a wrong answer wearing the face of a right one — a run that finishes, a report that looks healthy, and a number that is not true. They cost us real hours to find, which is why they are written down here rather than kept.

  • Email is the unique key on a contact

    The API answers 409, contact already exists. So the most obvious duplicate — the same person twice with the same address — is the one duplicate HubSpot cannot hold. The real ones look different: a record entered with no email at all, a nickname against a mistyped domain, or a name change where the address changed with it. A cleanup written for the obvious case finds nothing and reports a clean database.

  • Malformed addresses arrive by import, and only by import

    HubSpot checks the ending on write and refuses the ones that are nearly right. That means a whole class of bad data can only have arrived by import or migration — which tells you where to look, and it also means a test fixture cannot be loaded with the very thing you are trying to detect.

  • One field is separated by semicolons, and the wrong split reports zero

    The attachment ids on a note are one string joined by semicolons. Split it on a comma, as almost everything else in the world is split, and every lookup misses, and the report states that there are no attachments. The run succeeds, the number is wrong, and nothing anywhere says so.

  • A count is two operations that have to agree

    The list endpoint returns pages with no total in them, so the count is whatever the extraction happened to reach and a short page undercounts while returning a perfectly healthy response. The search endpoint does return a total. We take both and report whether they agree, because a disagreement is a finding and a silence is not.

  • A permission error can be about the request rather than the key

    One association route answered that the user does not have permission. The same key with the same scopes succeeded on the batch route with an explicit type id. The permissions screen was not where the answer was, and reading the error as a statement about the key sends you there for nothing.

  • Notes ride on the object permissions

    Searching the scope picker for notes returns nothing at all. Reading and writing them both ride on the object scopes, which is not what a careful reading of the documentation would lead you to request.

The limits that decide the shape.

  • Storage is generous. Computation is rationed.

    Millions of contacts are fine. Custom code inside a workflow gets twenty seconds and 128 MB, and HubSpot states that this is system-wide and cannot be changed. A card that renders inside a record has about five seconds to answer. Anything that needs real thinking has to be worked out somewhere else and be waiting by the time it is asked for.

  • It is eventually consistent, and the screen stays quiet about it

    HubSpot’s own documentation: it may take a few moments for newly created or updated objects to appear in search results. Typically seconds. Lists, workflows and reports all sit downstream of that index, so each one inherits the delay, and none of them shows a staleness indicator. A build that assumes a write is immediately readable will work in testing and fail on a busy day.

  • The search endpoint is the slow lane

    General requests burst at a hundred or two per ten seconds depending on tier. Search is four per second. Any job that leans on search is governed by that number and has to be designed around it rather than throttled into it afterwards.

  • Keeping the code outside HubSpot keeps it yours

    Their own serverless runtime shares a pool of execution time across a whole account, and HubSpot’s documentation states that an Enterprise subscription is required to install an app that uses it. A backend that lives on ordinary infrastructure has more time, works on every tier, and can be handed to your own developer. That is the recommendation whichever way the licence conversation goes.

What the work costs.

The platform changes how a job is built, not what it is worth, so these are the same figures as everywhere else on this site.

  • Records that disagree with each other

    A read-only report on what is actually in there at $390, the cleanup itself at $2,950, and a move onto or off the platform with the count proved on both sides at $4,900. The customer-record work.

  • Joining it to something else

    One connection in one direction at $3,250, or two systems kept in step both ways at $11,500. The integration work.

  • A job that starts itself

    One job at $2,550, or the whole chain around it at $3,950. Work that runs on its own.

When it is running, somebody has to be watching it.

A build is finished the day it goes live. Keeping it alive is a different job, and on a platform that indexes late and changes its API twice a year it is not an optional one.

Watch

$549 per month

“It runs. I do not want to be the one who notices when it stops.”

  • We are told the moment a run fails, and we tell you
  • A written note every month of what ran, what failed and what we did
  • Broken things fixed by the next working day
  • Your accounts stay yours — we hold access, not ownership

Guard

$849 per month

“I need to know about the runs that finish and quietly do nothing.”

  • Everything in Watch, plus:
  • We check the runs that finish but do nothing, where there is something to count
  • Broken things fixed the same working day
  • Changes are unlimited — they join a queue and we work through it in order

Managed

$1,200 per month

“I do not want to run it at all. I want somebody whose job it is.”

  • Everything in Guard, plus:
  • We operate it, not you
  • A named person, and the name of their backup
  • Reachable outside business hours when something is genuinely down
  • Your changes are worked ahead of everyone else’s

Monthly, in advance. Thirty days’ notice either way, from either side. Offered after a build, never before — we will not sell you a plan to watch something we have not seen.

Tell us what the CRM gets wrong.

The tier you are on, roughly how many records, and the thing somebody has stopped trusting. We will tell you whether it is a cleanup, a connection, or a setting you already pay for. We reply within one business day.

Describe the job The other platforms