> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trychert.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Limits

> HubSpot API rate limits, daily send capacity, retry semantics, and dedup windows.

The integration is bound by three independent rate limits — HubSpot's
API limits on outbound calls Chert makes into your account, Chert's
own per-project send capacity, and HubSpot's webhook delivery
guarantees.

## Before You Begin

| Concept             | Definition                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| HubSpot daily limit | The 24-hour rolling cap on API calls to your HubSpot account, set by your HubSpot edition.                                |
| HubSpot burst limit | The 10-second-window cap that protects HubSpot from spikes; usually 100 to 190 calls per 10 seconds depending on edition. |
| Chert daily cap     | The per-project per-day cap on first-touch sends, configured in **Settings → Auto-dial**.                                 |
| Idempotency key     | A stable token that lets a retry of the same logical operation return the prior result instead of re-sending.             |

## HubSpot API Calls Made by Chert

Each integration surface consumes a known number of HubSpot API calls.

| Surface                    | Per event | Endpoints                                                                                                                  |
| -------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| Sidebar card load          | 1         | `GET /crm/v3/objects/contacts/{id}`                                                                                        |
| Sidebar card send          | 1         | `GET /crm/v3/objects/contacts/{id}`                                                                                        |
| Workflow custom action     | 0         | The action callback delivers the data; Chert does not read back.                                                           |
| Contact creation auto-send | 1         | `GET /crm/v3/objects/contacts/{id}`                                                                                        |
| Reply push                 | 2         | `POST /crm/v3/objects/communications`, `PUT /crm/v4/objects/communications/{id}/associations/default/contacts/{contactId}` |
| OAuth token refresh        | 1         | `POST /oauth/v1/token` (does not count against CRM daily limit)                                                            |

A typical operator who opens 50 contact records, sends from 10 of
them, and receives 10 replies consumes roughly 80 HubSpot API calls
per day.

## Chert Send Capacity

| Limit                          | Default                                | Configurable              |
| ------------------------------ | -------------------------------------- | ------------------------- |
| Project daily cap              | Set per project during onboarding      | Yes, in the Chert console |
| Per-phone-line minimum gap     | 10 minutes between first-touch sends   | No                        |
| Per-phone-line first-touch cap | 30 sends per phone line per day        | No                        |
| Sending window                 | Per project, in the project's timezone | Yes                       |
| Auto-dial enabled              | Off until configured                   | Yes                       |

When the daily cap is reached, the workflow action returns
`ok = false` with `error = "daily cap reached"`. The sidebar card
returns the same shape and renders an inline notice. The contact
creation webhook logs the event with status `failed` and skips the
send.

Every HubSpot-facing failure body now also carries a numeric `code`,
a generic human `message`, a `retryable` boolean, and a `trace_id`
alongside `ok: false` / `error`. The numeric `code` follows the shared
[Messaging API error scheme](/api/errors). All of these routes return
HTTP `200` for logical failures — see Retry Semantics below.

## Retry Semantics

### From HubSpot to Chert

All four HubSpot-facing routes return HTTP `200` for **every** logical
failure — including signature verification failures — so HubSpot's
retry-on-non-2xx behavior never fires on a per-event error. The failure
is signalled in the body (`ok: false` with `code` / `message` /
`retryable` / `trace_id`, or `outputFields.ok: false` for the workflow
action). Branch on the body, not the HTTP status.

| Surface                          | HubSpot retry behavior                                                         | Chert response                                                                                                                                                                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `app.uninstalled` webhook        | Retries on 5xx with exponential backoff, up to roughly 24 hours.               | Always 200 OK. Success and logical failures (including signature rejection) both return `200` with `ok` reflecting the outcome.                                                                                                               |
| `contact.creation` webhook       | Retries on 5xx; one delivery per retryable failure.                            | Always 200 OK to discourage retries on per-event failures. Per-event errors are logged with status `failed` and returned in the body with `ok: false` + `code`.                                                                               |
| Workflow custom action (`/send`) | Retries on 5xx; the workflow execution itself stalls until the action returns. | Always 200 OK with `outputFields.ok` reflecting the actual outcome. The workflow author branches on `ok` / `code`, not on HTTP status. (Previously this route returned non-200 on some failures, which broke HubSpot's contract — now fixed.) |
| Sidebar UI extension             | No retry — the operator clicks again.                                          | Always 200 OK with the `ok` flag plus `code` / `message` / `retryable` / `trace_id` on failure.                                                                                                                                               |

### From Chert to HubSpot

| Path                       | Retry behavior                                                                                                                                                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reply push                 | One inline retry on 401 after a forced token refresh. No further retries from this code path. The reply-notify cron re-runs every two minutes; transient HubSpot failures retry on the next tick. |
| Reply push circuit breaker | Five consecutive failures trip the breaker for the tenant. Subsequent pushes are skipped until a successful push resets the counter.                                                              |
| OAuth token refresh        | One attempt per call site. A persistent refresh failure surfaces to the caller.                                                                                                                   |

### Inside the Chert pipeline

| Path                   | Retry behavior                                                          |
| ---------------------- | ----------------------------------------------------------------------- |
| Outbound send dispatch | Handled by the Chert delivery pipeline, transparent to the integration. |
| Lead resolve           | No retry. Failure is returned to the caller as a structured error.      |

## Idempotency

| Source                   | Key                                              | Effect on retry                                                                                |
| ------------------------ | ------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| Workflow custom action   | HubSpot's `callbackId`                           | Same callback always returns the prior response.                                               |
| Sidebar card send        | `widget:{tenantId}:{contactId}:{secondBucket}`   | Double-clicks within the same second collapse to one send; the next second is a fresh attempt. |
| Contact creation webhook | `webhook:contact_creation:{objectId}`            | The same contact creation event never re-fires.                                                |
| Chert internal pipeline  | `(phone_line, recipient, message)` for six hours | Identical messages within six hours dedup at the dispatch layer.                               |

A response carrying `duplicate: true` is a successful outcome and
should be treated identically to `duplicate: false` by downstream
workflow branches.

## Sandbox versus Production

HubSpot's developer sandbox accounts are first-class for this
integration. The same install flow, scopes, and signature scheme
apply. Differences to plan for:

| Concern            | Sandbox                                        | Production                          |
| ------------------ | ---------------------------------------------- | ----------------------------------- |
| Rate limit         | Lower than production                          | Edition-dependent; see HubSpot docs |
| Webhook delivery   | Delayed under load                             | Real-time                           |
| Phone line         | Use a Chert test project with test phone lines | Use the production project          |
| Workflow execution | Manual triggering only                         | Triggered by real contact events    |

Chert provisions a separate tenant per HubSpot account regardless of
sandbox or production status. A sandbox install does not consume a
production install slot.

## See Also

* [Architecture](/hubspot/architecture) — where each limit applies in the call graph.
* [Configuration](/hubspot/configuration) — daily cap and sending window settings.
* [Security](/hubspot/security) — what is logged for retried events.
