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

# Create Custom Links

> Create personalized links in batches, generate thumbnails, and track asynchronous jobs.

The Custom Links API creates the same personalized experiences available through
CSV Context Pack imports. One request can accept from 1 to 500 links and returns
an asynchronous job to poll.

## Create a key

Create an API key in Hobbes Settings with:

* `custom_links:write` to create and manage links, jobs, thumbnails, and campaigns
* `custom_links:read` for read-only listing and polling

Write scope also satisfies read operations. Existing keys do not receive either
scope automatically.

## Create links

Send one `POST /api/v1/custom-links` request for the entire batch. A 300-link
batch is one HTTP request, but it consumes 300 creation units and, when
`generateThumbnails` is true, up to 300 render units.

```bash theme={null}
curl "https://api-us.hihobbes.com/api/v1/custom-links" \
  -X POST \
  -H "Authorization: Bearer $HOBBES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crm-export-2026-07-23-01" \
  --data '{
    "campaign": {"name": "Summer outreach"},
    "generateThumbnails": true,
    "links": [
      {
        "externalId": "crm-contact-1042",
        "firstName": "Ada",
        "lastName": "Lovelace",
        "email": "ada@example.com",
        "phone": "+1 415 555 0100",
        "role": "VP of Operations",
        "company": "Example Co",
        "context": "Interested in automating lead follow-up.",
        "shareTitle": "A personalized walkthrough for Example Co",
        "shareDescription": "See the workflow we discussed.",
        "shareThumbnailUrl": "https://cdn.example.com/share-image.png",
        "prospectLogoUrl": "https://cdn.example.com/example-logo.png",
        "prospectDomain": "example.com",
        "displayName": "Ada at Example Co",
        "active": true
      }
    ]
  }'
```

`campaign` can identify an existing campaign by id or create/reuse one by name.
The personalization fields match the CSV import contract. `externalId`,
`displayName`, and `active` are API-specific.

`prospectDomain` identifies the prospect and can help logo discovery. It does
not choose the published link's delivery domain. Hobbes publishes through the
organization's active custom domain or configured Sales Agent URL. Creation
returns `409` when neither is configured.

<Warning>
  Only provide public HTTP or HTTPS image URLs. Hobbes rejects private-network
  targets, unsafe redirects, unsupported content types, and images larger than
  10 MB.
</Warning>

## Preserve idempotency

Creation, thumbnail regeneration, and explicit retry operations require an
`Idempotency-Key`. Generate a stable key for one logical operation and keep it
with your integration state.

* Replaying the same key with the same request returns the original job.
* Reusing the key with different content returns `409`.
* An idempotent replay does not consume quota again.

## Poll the job

Creation returns `202 Accepted` with:

* A job summary in the response body
* `Location: /api/v1/custom-link-jobs/{job_id}`
* `Retry-After: 2`

Poll the `Location` every two seconds. After 30 seconds, back off to every five
seconds. Store the response `ETag` and send it on the next request:

```bash theme={null}
curl "https://api-us.hihobbes.com/api/v1/custom-link-jobs/$JOB_ID" \
  -H "Authorization: Bearer $HOBBES_API_KEY" \
  -H "If-None-Match: $JOB_ETAG"
```

An unchanged job returns `304 Not Modified` without a response body. Terminal
statuses are `completed`, `completed_with_errors`, `cancelled`, and `failed`.
Non-terminal statuses are `queued` and `processing`.

Fetch `GET /api/v1/custom-link-jobs/{job_id}/items` when counters change or the
job becomes terminal. Items remain in input order and independently report:

* `creationStatus` for validation and link creation
* `thumbnailStatus` for requested thumbnail work
* The Custom Link id and published URL when creation succeeds
* Structured validation, creation, or thumbnail errors when work fails

Request-level errors reject the whole request. Item-level validation errors
allow otherwise valid links to proceed, so a terminal job may be
`completed_with_errors`.

## Manage links and campaigns

Use the list and detail endpoints to filter by campaign, status, external id,
email, company, date, or search text. `PATCH /api/v1/custom-links/{id}` updates
personalization, sharing metadata, campaign membership, or active state.
Thumbnail work is queued only when an affected field changes.

Bulk actions can activate, deactivate, or move up to 500 links. Hard deletion
is not exposed. Deactivation preserves historical attribution.

Campaign endpoints create, list, rename, retrieve, and delete organizational
folders. Deleting a campaign unfiles its links without deleting them.

## Read engagement

Every Custom Link list and detail response includes an `engagement` rollup.
The link-level rollup combines all activity associated with that link; it is
not the journey of one person or browser. The lifecycle uses the same
definitions in link and session responses:

| Stage     | Definition                                                                          |
| --------- | ----------------------------------------------------------------------------------- |
| `opened`  | At least one non-bot `widget_loaded` page view                                      |
| `started` | A session recording exists for the link                                             |
| `engaged` | A visitor spoke, typed a message, clicked a suggestion, or continued the experience |

Stages only advance. Lead submission, meeting click, and meeting booking are
reported as conversion flags rather than additional stages. Because they prove
downstream human activity, they also advance a link or session to `engaged` when
an earlier client event was missed. `botScannedOnly` identifies a server-side
link resolution with no observed human open or downstream activity.

Use `GET /api/v1/custom-links/{id}/sessions` to retrieve the sessions associated
with a link. The result intentionally excludes transcripts and visitor PII.
Full session detail still requires `sessions:read`; transcripts require the
separate `transcripts:read` scope.

`loadCount` counts accepted non-bot load events, while `pageViewCount` deduplicates
those events by `pageViewId`. Full-screen opens are recorded independently of
the real-time conversation connection.

`uniqueVisitorCount` is the best available browser-continuity estimate. When
consent and browser storage allow it, Hobbes uses a persistent anonymous browser
identifier. A new browser, device, domain, private window, or cleared storage
may still count separately; the identifier does not identify a person.

Use `visitorCountMode` to interpret the total:

| Mode         | Meaning                                                                                 |
| ------------ | --------------------------------------------------------------------------------------- |
| `identified` | Every counted visit had a persistent anonymous browser identifier                       |
| `estimated`  | One or more ephemeral page views contributed because persistent storage was unavailable |

`persistentUniqueVisitorCount` and `ephemeralPageViewCount` remain exact raw
counts in either mode. When the mode is `estimated`, treat
`loadsPerUniqueVisitor` as approximate because Hobbes cannot establish whether
ephemeral page views came from the same returning browser.

Use `GET /api/v1/custom-link-campaigns/{id}/engagement` for raw campaign totals.
A campaign response includes generated, opened, started, engaged, lead,
meeting-click, meeting-booking, bot-only, load, page-view, visitor, and session
counts. It uses the same visitor certainty fields as each link.

UTM attribution is session-level. Retrieve a session with
`GET /api/v1/sessions/{session_id}` and read `tracking.utmSource`,
`tracking.utmCampaign`, `tracking.utmMedium`, `tracking.utmTerm`, and
`tracking.utmContent`. Link and campaign engagement responses do not aggregate
UTM values.

A generated link is not proof that an email was sent, delivered, or not bounced;
join those denominators from the sending platform before calculating upper-
funnel conversion rates.

## Regenerate, cancel, or retry

* `POST /api/v1/custom-links/{id}/thumbnail-jobs` explicitly regenerates one thumbnail.
* `POST /api/v1/custom-link-jobs/{id}/cancel` requests best-effort cancellation of unfinished work.
* `POST /api/v1/custom-link-jobs/{id}/retry` creates a child job for failed or selected items.

Completed work remains available after cancellation. Cancellation does not
refund quota. Explicit thumbnail retries consume new render units; Hobbes'
internal retries do not.

## Plan for limits

The standard per-key limit is 120 HTTP requests per minute. Additional
organization-wide protections apply:

| Limit                                   | Default                                                   |
| --------------------------------------- | --------------------------------------------------------- |
| Creation, thumbnail, and retry requests | 10 per minute                                             |
| Job polling                             | 300 per minute across the organization, still 120 per key |
| Accepted links                          | 2,000 per UTC hour and 10,000 per UTC day                 |
| Thumbnail render units                  | 2,000 per UTC hour and 10,000 per UTC day                 |
| Unfinished thumbnail items              | 1,000 per organization                                    |

Invalid items do not consume weighted quota. Hobbes validates the whole request
before admission; if all otherwise valid items would exceed a quota, the request
returns `429` rather than accepting an arbitrary partial batch.

On `429`, wait for `Retry-After` and inspect the remaining quota and reset
headers before retrying. A `503` indicates the global queue or worker circuit
breaker is unavailable; no quota is charged for rejected work.
