← Documentation to help get you started

PartnerAPI

Updated October 30, 2026

Partner API

The partner API lets an agency's own tools start demo builds: an assistant built from a prospect's public website, with a test page link and a list of the common questions it could not answer. The contract is published as OpenAPI at <BASE_URL>/v1/openapi.yaml (no key needed to read it).

Plan and design decisions: docs/plans/ProvisioningAPIPlan.md. How demo builds work end to end, plus the test runbook: Demo builds.

Test tools (tools/partner-api/):

File Use
test-demos.ps1 Scripted end-to-end check: refusals, a real build, idempotency, polling, delete. Exits non-zero on failure.
demos.http Every request by hand, for the VS Code REST Client extension.
verify-signature.js Verify a captured callback's X-ChatOps-Signature, or sign a sample body.

Keys

  • Created by an agency manager in Agency Console → Demo builds → API keys.
  • Every key expires: 30, 90 or 180 days. Six months is the hard maximum. To rotate, create a new key, switch your tools over, then revoke the old one.
  • The key (cop_live_…) and its webhook signing secret (whsec_…) are shown once, at creation. We store only a hash of the key. The signing secret is stored encrypted (INTEGRATION_ENCRYPTION_KEY); without that variable, keys cannot be created.
  • Send the key as Authorization: Bearer cop_live_…, or as X-API-Key for tools that only allow a plain header.
  • 60 requests per minute per key.
  • A key can carry a default callback URL, used when a request does not name its own callbackUrl.

Endpoints

Method Path What it does
POST /v1/demos Start a build. 202 with the new build; 200 with the existing one when externalRef was used before.
GET /v1/demos?externalRef=… Find a build by your own id.
GET /v1/demos/{id} Read a build.
DELETE /v1/demos/{id} Delete it now. The link stops working immediately.

Request body for POST /v1/demos:

{
  "businessName": "Calm Harbor Therapy",
  "websiteUrl": "calmharbortherapy.com",
  "contactEmail": "owner@calmharbortherapy.com",
  "externalRef": "ghl_contact_8f2k1",
  "industry": "therapy",
  "region": "US",
  "callbackUrl": "https://services.leadconnectorhq.com/hooks/…"
}

Only websiteUrl is required. industry defaults to therapy, region (which crisis lines the demo shows: US or CA) to US.

status is building, then success or failed. A failed build carries a reason and never a testPageUrl:

reason meaning
invalid_url The address is not a public website.
crawl_blocked The site refused us (robots.txt, 403, a bot wall).
no_content Pages loaded but had no readable text.
screenshot_failed The homepage could not be captured.
ingest_failed The knowledge base could not be built.
timeout / internal Our side failed. Safe to retry with a new externalRef.

Retrying with the same externalRef returns the original build, including a failed one. To rebuild, DELETE it first or use a new reference.

Limits

  • 50 builds per agency per 24 hours. One build of the same site per 10 minutes.
  • At most 15 pages of the site are read. robots.txt is respected.
  • Unlike every other crawl in the product, a demo build does not require a verified domain. The page cap is the mitigation.
  • Demos are deleted 14 days after creation. The link stops working on that date, and an hourly worker sweep then erases the workspace: knowledge base, vectors, chats and files.

Callbacks

When a build finishes, successfully or not, cmd/worker POSTs the result to the callback URL: the same fields as GET /v1/demos/{id}, plus "event": "demo.completed" and a deliveryId. Fields worth knowing for no-code tools:

  • answers: {"insurance": "not_answered", "fees": "answered", …}, so each result can be mapped to its own contact field by JSON path.
  • unansweredSummary: the unanswered questions, one per line, ready for an email.
  • externalRef: your id, to match the result to the contact.

Signature: X-ChatOps-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of <t>.<raw request body> keyed with the webhook signing secret. Reject a mismatch, or a t more than 5 minutes old. partnerapi.Verify is the Go reference implementation.

Any 2xx response counts as delivered. Anything else is retried after 1 min, 5 min, 15 min, 1 h, 3 h and 6 h, then marked failed. Callback status shows on each build in the Agency Console.

GoHighLevel recipe

Full step-by-step setup, field mapping and curl tests: Connecting GoHighLevel. In short:

  1. Workflow action Custom Webhook: POST <BASE_URL>/v1/demos, header Authorization: Bearer cop_live_…, JSON body with externalRef set to the contact id.
  2. Separate workflow with an Inbound Webhook trigger. Put its URL in the API key's callback URL (or send it as callbackUrl).
  3. In that workflow, find the contact by externalRef and map status, testPageUrl, unansweredSummary and answers.* onto custom fields. Branch on status: send the delivery email on success, notify yourself on failed.

GHL's inbound webhook cannot verify the signature itself. The callback carries nothing secret beyond the demo link, which is unlisted and expires.

What the demo is (and is not)

A demo workspace is a separate tenant (tenants.is_demo=1) owned through demo_builds.organization_id, not as an agency subaccount. It collects no contact details (no pre-chat form, no AI contact capture, no transcript email) and sends no alerts. The central console hides demo workspaces unless "Show demo workspaces" is ticked. Its crisis message lists 911 and 988 and says that nobody is monitoring the chat.