A HubSpot sales workflow can request a subscription upgrade, but it cannot activate one. A step-by-step look at a synthetic upgrade flow where the boundary refuses a correctly signed request from the wrong identity.
· Article
Every growing software company ends up with the same shape of automation. Sales works in the CRM. Billing and subscriptions live in a separate service. A workflow connects the two, so a rep can move a customer to a bigger plan without filing a ticket.
The convenient way to build that workflow is to give it one credential that can reach the subscription service. From that moment, anything the subscription service allows, the CRM workflow can do: request an upgrade, approve it, activate it, discount it. Separation of duties becomes a matter of which buttons the CRM shows, not what the system will actually accept.
This post walks through a recorded demo that tests that line directly, and explains what the boundary checked, why one correctly signed request was refused, and what the audit trail shows at the end.
Most API security asks one question: is this caller who it says it is? For a CRM workflow the answer is almost always yes. The request comes from the real integration, with a real signature, from the real network. Nothing about it is forged.
The harder question is whether this caller should be allowed to perform this specific operation. A sales identity that may submit an upgrade request should not be able to approve it, and certainly not activate it. Those are different responsibilities, owned by different teams, and auditors expect them to stay separate.
When a single shared credential stands in for the whole integration, that distinction exists only in application code and team habits. One misconfigured workflow, one over-eager automation, or one compromised CRM account is enough to cross it.
The scenario is synthetic: a made-up customer, Aster and Finch Studio, wants to move from the Starter plan to Professional. It is built to show fit for SaaS operations, not a customer deployment, and no real account or invoice is involved.
Sales starts in HubSpot. Behind it are two separate services: a subscription service that owns plans and billing, and an operations service that owns approvals. Trustplane sits in front of them, and each workflow step calls through it as a distinct workload identity.
The workflow checks the customer's saved subscription and billing requirements, confirms the customer is eligible, and submits the upgrade request. That call is within what the sales identity is allowed to do, so the boundary admits it.
Back in HubSpot, the record shows a saved request pending approval. The subscription itself is still on Starter. Sales has moved the customer forward, which is its job, but activation remains a separate responsibility.
Next the demo deliberately tries to activate Professional using the sales identity. This is the shortcut a well-meaning rep or a misbuilt workflow might take: the customer is eligible, the request is saved, why not just flip the plan?
The request is correctly signed. The identity is real. But the policy for the activation operation does not include the sales workload, so the boundary denies it. The audit record shows the subscription service was never reached; the refusal happened before the API saw anything.
The line the demo uses here is the point of the whole post: a valid identity does not mean unrestricted access.
Every request through Trustplane carries a short-lived passport bound to the caller, the action, the audience, the route, and a moment in time. The boundary checks all of them locally, with no hosted call in the request path. See how the boundary works for the full sequence.
Which workloads may call which operations is declared up front as a trust anchor policy: the sales workload may call the request route; the operations workload may call the activation route. Because the passport is bound to the route and action, a sales passport cannot be reused to reach the activation route, even though every other check would pass.
That moves separation of duties out of the CRM's configuration and into the boundary in front of the subscription service. It holds no matter which tool, script, or agent produced the request.
Request-level view
Allow
Approved action, scope, brand, and workload all match.
Refuse
Wrong route, tenant, scope, or replayed proof.
one request · one verdict · one audit record
Now an authenticated operator invokes the separate operations service. It approves this specific customer request, not upgrades in general. The subscription service checks that approval before allowing the plan to change.
This call comes from a different workload identity, one the policy does allow to activate. The boundary admits it, the subscription service performs the change, and the result is recorded.
The CRM never has to be trusted with the change itself. HubSpot reads the result back from the subscription service, and the same request now shows Professional activated, with approval attributed to the authorized operations service.
These are the native outputs of the live workflow, not a mock-up. Sales sees the outcome in the tool it already uses, without ever holding the authority to cause it.
Trustplane control records both halves of the story independently of the workflow. The denied activation from sales is there with its reason. The allowed activation from operations is there too, with the subscription service returning success.
Refusals are recorded on the same terms as approvals. For an auditor, that is the evidence that separation of duties is enforced, not just documented: the wrong identity tried, was stopped, and the right identity completed the change.
For revenue teams, sales can keep moving customers forward without waiting on engineering, because requesting an upgrade is explicitly allowed. Nothing about their workflow gets slower.
For finance and compliance, approval and activation stay with operations, and that is enforced at the API, not by convention. Every attempt, allowed or refused, is a record with a reason, which is what SOC 2 and internal audit reviews ask for.
For platform teams, there is no shared integration key to rotate or leak. Each workflow step is its own proven identity, and changing who may activate plans is one policy edit, not a credential hunt.
The same pattern applies wherever a CRM or automation tool sits next to a system with real consequences: issuing refunds or credits, applying discounts, changing billing details, extending trials, or provisioning seats. Request and execute become separate operations with separate allowed callers.
It applies equally to AI agents. An agent working inside a CRM is another machine identity making requests. It can be allowed to propose changes without being allowed to make them, and the boundary judges each request the same way, whatever produced it.
If your CRM, billing, or subscription automation needs real separation of duties, book a custom demo. More scenarios are on the use cases page.
Every request proves itself — no keys, anywhere.
Put the boundary in front of one API and measure it yourself.