Skip to main content
Component map, call graph, and synchronous-versus-asynchronous behavior of the integration.

System Landscape

System landscape A connected HubSpot account issues outbound HTTPS to the Chert messaging service from three places — the sidebar UI extension on a contact record, the workflow custom action, and the contact.creation webhook. Chert dispatches the send through its delivery infrastructure and posts replies back into HubSpot as Communication engagements on the contact timeline. There is no shared infrastructure between HubSpot accounts. Every request is verified against the per-account signing secret before any work runs.

Components

Server routes

Server modules

Data model

Outbound Send Sequence

Send sequence
  1. A workflow, the sidebar card, or the contact-creation webhook hits a Chert ingress route.
  2. The route reconstructs the full request URL and verifies the HubSpot Signature v3 header against the timestamp and the raw body.
  3. The route loads the tenant by HubSpot account ID. Inactive tenants short-circuit with unknown or inactive tenant.
  4. The route checks the idempotency key in hubspot_send_events. A prior row returns its stored response verbatim.
  5. The route resolves a Chert lead. Sidebar sends always auto-create; workflow sends auto-create only when the per-account flag is on.
  6. The route renders the message. The sidebar passes the operator’s text through unchanged. The workflow action passes a token-rendered string from HubSpot’s workflow editor. The contact-creation webhook renders the project’s CRM template against the new contact’s name and company.
  7. The route hands off to Chert’s internal send pipeline, which selects a phone line, enforces the 10-minute minimum gap, deduplicates within a 6-hour rolling window, and dispatches through Chert’s delivery infrastructure.
  8. The route writes the outcome to hubspot_send_events and returns the response shape required by the caller.
End-to-end latency in the synchronous path is dominated by Chert’s internal pipeline; typical observed latency is 200 to 600 ms.

Inbound Reply Sequence

Reply sequence
  1. A recipient replies. Chert’s internal reply pipeline resolves the lead and stamps the inbound message on its conversation log.
  2. The reply-notification cron picks up the new inbound and fans out to email, Slack, the customer webhook (if configured), and the HubSpot push.
  3. The HubSpot push loads the tenant and confirms the lead carries a hubspot_tenant_id and hubspot_contact_id. Without both, the push is a no-op.
  4. The push obtains a valid access token, refreshing if the cached token is within 60 seconds of expiry.
  5. The push POSTs a Communication engagement to /crm/v3/objects/communications with hs_communication_channel_type = SMS.
  6. The push associates the new Communication to the contact via /crm/v4/objects/communications/{id}/associations/default/contacts/{contactId}. The default association resolves the correct association type ID server-side.
  7. On success, the per-tenant inbound_consecutive_failures counter resets and inbound_last_success_at is stamped. On failure, the counter increments. Five consecutive failures trip the breaker and silence the push until the next successful send through the tenant.
The reply path is fire-and-forget from the cron’s perspective — a dead HubSpot account never blocks operator email or Slack notifications.

OAuth Token Refresh

HubSpot access tokens expire after roughly 30 minutes. Chert never refreshes ahead of time on a schedule; instead, every code path that calls HubSpot acquires a token through the same helper. A 401 response from HubSpot triggers a single one-shot refresh and retry, in case the cached token expired between read and call.

Synchronous versus Asynchronous Boundaries

Idempotency

Beneath these per-route keys, Chert’s internal send pipeline applies its own 6-hour rolling dedup on (phone_line, recipient, message).

See Also

  • Configuration — settings exposed in the Chert console.
  • Security — signature verification, token storage, scope rationale.
  • Limits — rate limits, daily caps, retry semantics.