API guide
For integration teams

GCC API guide

Every webhook that connects GoHighLevel to the Growthlio Command Centre: what each one does, when to call it, what to send and what comes back. 9 endpoints, no login needed.

Jump to an endpoint

Start here

GCC (the Growthlio Command Centre) is the brain behind each client account. GoHighLevel stays the place where contacts, calendars and messages live; GCC does the thinking (AI replies, calls, tasks, ad signals) and GHL tells it what happened by calling the endpoints on this page. Every endpoint is an HTTPS POST to https://command.growthlio.com with a JSON body.

Provisioning secret

One per agency, sent as x-provision-secret. Only for setting up new clients.

Account secret

One per client account, in the GHL custom value replio_webhook_secret. The snapshot workflows use it.

Workspace token

A bearer token for the ads outcomes endpoint, issued by Growthlio.

  • Field names are forgiving: camelCase, snake_case and GHL nested shapes (contact.id, location.id) are all accepted, top level or under customData.
  • A merge tag GHL could not fill in (a literal {{ .. }}) counts as empty, not as a value.
  • Retry any 5xx 2 to 3 times, a few seconds apart. Every endpoint here is safe to repeat unless it says otherwise.
  • A 200 can still mean "nothing to do" (duplicate, unmatched, skipped). Read the status field, not just the HTTP code.
  • Secrets and tokens are given to you privately and never appear on this page. Keep them out of client visible config and source control.

Client setup

Bring a new paying client into GCC.

Provision a new client

POST/api/clients/provision

One call when a new client pays and their GHL sub-account exists. GCC creates the access key it needs for that sub-account, creates (or reuses) the client in your workspace and returns the client onboarding link for you to send. Round trip is one to two seconds.

When to call it

Last step of your payment or sign-up automation. Safe to call again for the same sub-account, so it can also sit on a "retry if setup incomplete" path.

Called by

Your GHL agency or SaaS workflow (Webhook action), or n8n.

Auth: Shared provisioning secret

Header x-provision-secret. Growthlio gives you this privately; it is never in this guide. The workspace is decided from your companyId on our side, so a call can only ever set up accounts in your own agency.

Body fields

  • locationIdRequiredalso: location_id

    The new client GHL sub-account id. In a sub-account workflow: t1xDJ87jWjC1gmCMpg04.

  • companyIdRequiredalso: company_id

    Your GHL agency id. Same every time, hard-code it. An agency Growthlio has not mapped is refused.

Example request

curl
curl -X POST https://command.growthlio.com/api/clients/provision \
  -H "x-provision-secret: YOUR_SECRET" \
  -H "content-type: application/json" \
  -d '{"locationId":"abc123SUBACCOUNT","companyId":"YOUR_AGENCY_ID"}'

Success response (200)

JSON
{
  "ok": true,
  "clientAccountId": "144a8e0c-...",
  "created": true,
  "onboardingUrl": "https://command.growthlio.com/start/XXXX",
  "emailed": false
}

Errors and retrying

  • 400

    Missing or malformed locationId / companyId.

    Fix the body. Not retryable as is.

  • 401

    Bad or missing x-provision-secret.

    Check the secret header.

  • 403

    That agency is not mapped to a workspace.

    Send Growthlio the companyId to map.

  • 409

    Growthlio access to that agency needs reconnecting.

    Tell Growthlio. Retry after they confirm.

  • 413

    Body over 16 KB.

    Send only the two fields.

  • 502

    Brief GHL hiccup upstream.

    Retry 2 to 3 times, a few seconds apart.

  • 503

    Provisioning temporarily unavailable.

    Retry after a short wait.

Safe to repeat?

Safe to repeat. A second call for the same sub-account reuses the existing key and onboarding link. created tells you which happened (true = new, false = already set up).

Set it up in a GHL workflow

  1. In the workflow, after payment, add a Webhook (custom webhook) action.
  2. Method POST, URL https://command.growthlio.com/api/clients/provision.
  3. Headers: x-provision-secret = the secret Growthlio gave you, content-type = application/json.
  4. Raw JSON body: {"locationId":"t1xDJ87jWjC1gmCMpg04","companyId":"YOUR_AGENCY_ID"}.
  5. Save the response and use onboardingUrl in the next step to text or email the client.
  • Send onboardingUrl to the client yourself (SMS or email). GCC also emails it to the sub-account business email when one is set (emailed: true), but that is only a backup.
  • Always retry 5xx before alerting a human.

Conversations

Lead replies, timeline events and bookings coming in from GHL.

Inbound lead message

POST/api/replio/webhook

The main conversation path. A lead replies by SMS, Instagram or Messenger, GHL forwards it here, and the AI agent reads the thread, decides and answers in GHL. Tags, bookings and handoffs happen inside the same call.

When to call it

On every inbound customer message in an account with an AI agent. The Command Centre snapshot already carries this workflow.

Called by

GHL workflow Webhook action (Customer Replied trigger), including the split account forward from a client-owned location.

Auth: Per account secret

Header X-Replio-Webhook-Secret. In a GHL workflow use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E; GCC writes that custom value when the account is deployed. The secret only works for its own account.

Body fields

  • locationIdRequiredalso: location_id location.id

    The GHL sub-account id.

  • contactIdRequiredalso: contact_id contact.id

    The GHL contact id of the lead.

  • messageRequiredalso: message.body body text Lead Response

    The lead message. Lead Response wins when it holds the combined double text. Not needed when media or a bump is sent.

  • audio_urlalso: attachment_url image_url

    Media instead of (or with) text.

  • rolealso: Active Agent requested_role

    Pin which agent answers: dbr, stl, rebook, showup, review, collections, cold_sms.

  • turn_typealso: Turn Type

    bump = a proactive follow up with no new lead message.

  • firstName, lastName, phone, email, tags

    Contact details, top level or under contact. tags can be an array or a comma separated string.

  • replio_test

    true returns a diagnosis without sending anything.

Example request

curl
curl -X POST https://command.growthlio.com/api/replio/webhook \
  -H "X-Replio-Webhook-Secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"locationId":"t1xDJ87jWjC1gmCMpg04","contactId":"{{contact.id}}","message":"","firstName":"{{contact.first_name}}","tags":""}'

Success response (200)

JSON
{
  "ok": true,
  "pipeline_run_id": "...",
  "status": "completed",
  "agent_reply_text": "...",
  "tags_added": ["..."],
  "tags_removed": [],
  "booking_event_id": null,
  "halt_stage": null,
  "halt_reason": null
}

Errors and retrying

  • 400

    invalid_json or missing location, contact or message.

    Fix the webhook body.

  • 401

    Missing or wrong secret.

    Use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E.

  • 402

    halt_reason subscription_required.

    The account subscription is not active. Talk to Growthlio.

  • 429

    Over 600 calls a minute per IP or per location.

    Back off and retry.

Safe to repeat?

No route level dedupe, but a turn that was already answered halts with double_text_already_answered, so a repeat does not double reply. Every raw payload is logged.

  • A 200 with halt_reason set is normal: the agent decided not to answer (opted out, human took over, outside hours and so on).
  • Can take up to 300 seconds on long turns. Do not set a short timeout on the GHL action.

Lead timeline event

POST/api/os/events

Records something that happened to a lead (booked, confirmed, no show, hot lead and so on) on its timeline in Growthlio OS. Action kinds also create a staff call task and a notification.

When to call it

From the webhook steps in the AI Command Centre snapshot, at each stage of the booking and show-up flow.

Called by

GHL workflow Webhook action in the snapshot.

Auth: Per account secret

Header X-Replio-Webhook-Secret. In a GHL workflow use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E; GCC writes that custom value when the account is deployed. The secret only works for its own account.

Body fields

  • contact_idRequiredalso: contactId

    The GHL contact id.

  • location_idRequiredalso: locationId location.id

    The GHL sub-account id.

  • kindRequired

    Action kinds (make a task): not_confirmed_2h, not_confirmed_1h, hot_lead, new_lead_silent, call_no_show, call_canceller, check_cancellation, slot_still_held, check_appointment. Activity kinds (timeline only): booked, confirmation_sent, reminder_sent, at_risk, confirmed, voice_requested, showed, no_show, recovered, cancelled, no_recovery, call_quality_good and others.

  • appointment_idalso: calendar.appointmentId

    Ties the event to one booking.

  • appointment_timealso: calendar.startTime

    Booking start time.

  • contact_namealso: first_name + last_name full_name

    Shown on the task card.

  • phone, timezone, step, set_id, tenant_id

    Optional context. set_id is cross checked against the location.

Example request

curl
curl -X POST https://command.growthlio.com/api/os/events \
  -H "X-Replio-Webhook-Secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"contact_id":"{{contact.id}}","location_id":"t1xDJ87jWjC1gmCMpg04","kind":"not_confirmed_2h","appointment_id":"{{appointment.id}}","appointment_time":"{{appointment.start_time}}","contact_name":"{{contact.name}}","phone":"{{contact.phone}}"}'

Success response (200)

JSON
{
  "status": "recorded",
  "id": "...",
  "task": true
}

Errors and retrying

  • 400

    Body missing a required field or not JSON.

    Fix the webhook body. Not retryable as is.

  • 401

    Missing or wrong X-Replio-Webhook-Secret.

    Check the header uses the account custom value.

  • 403

    set_id does not belong to this location.

    Remove set_id or send the right one.

  • 503

    A lookup or write failed on our side.

    Retry 2 to 3 times, a few seconds apart.

Safe to repeat?

One row per account, contact, kind and appointment. A retry returns status duplicate with HTTP 200.

  • An unknown location returns 200 with status unmatched, so a misrouted workflow does not error loop.

Still booked check

POST/api/os/appointment-check

After a cancellation, checks whether the lead still holds another live booking. If yes, GCC tags the contact "appointment | still booked" so the rebooking sequence can stop.

When to call it

Top of the Cancelled Rebooking Sequence in the snapshot.

Called by

GHL workflow Webhook action in the snapshot.

Auth: Per account secret

Header X-Replio-Webhook-Secret. In a GHL workflow use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E; GCC writes that custom value when the account is deployed. The secret only works for its own account.

Body fields

  • contact_idRequiredalso: contactId

    The GHL contact id.

  • location_idRequiredalso: locationId location.id

    The GHL sub-account id.

  • appointment_idalso: calendar.appointmentId

    The booking that was cancelled, so it is not counted.

  • set_id, tenant_id

    Optional.

Example request

curl
curl -X POST https://command.growthlio.com/api/os/appointment-check \
  -H "X-Replio-Webhook-Secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"contact_id":"{{contact.id}}","location_id":"t1xDJ87jWjC1gmCMpg04","appointment_id":"{{appointment.id}}"}'

Success response (200)

JSON
{
  "status": "checked",
  "checked": true,
  "stillBooked": false
}

Errors and retrying

  • 400

    Body missing a required field or not JSON.

    Fix the webhook body. Not retryable as is.

  • 401

    Missing or wrong X-Replio-Webhook-Secret.

    Check the header uses the account custom value.

  • 403

    set_id does not belong to this location.

    Remove set_id or send the right one.

  • 503

    A lookup or write failed on our side.

    Retry 2 to 3 times, a few seconds apart.

Safe to repeat?

Read and tag only. Safe to repeat.

  • If the calendar read fails the answer is still 200 with checked: false. Treat that as "not still booked".

Opportunity changed

POST/api/ghl/opportunity

Keeps GCC pipeline view in step with GHL in near real time when an opportunity changes stage or status. Without it, GCC catches up on its twice daily sync.

When to call it

Opportunity Status Changed or Pipeline Stage Changed trigger, only in accounts where live pipeline numbers matter.

Called by

GHL workflow Webhook action.

Auth: Per account secret

Header x-replio-ghl-secret, value Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E. A missing header is refused before anything is read.

Body fields

  • locationIdRequired

    The GHL sub-account id.

  • opportunity.idRequiredalso: opportunityId id

    The opportunity id. The body may be flat or nested under opportunity.

  • pipelineId, pipelineStageId, status

    Where it sits now.

  • name, monetaryValue, contactId, createdAt, updatedAt, lastStageChangeAt

    Optional detail.

Example request

curl
curl -X POST https://command.growthlio.com/api/ghl/opportunity \
  -H "x-replio-ghl-secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"locationId":"t1xDJ87jWjC1gmCMpg04","opportunityId":"","pipelineId":"","pipelineStageId":"","status":"","monetaryValue":""}'

Success response (200)

JSON
{
  "ok": true,
  "matched": true,
  "written": true
}

Errors and retrying

  • 400

    Missing locationId or opportunity id.

    Fix the body.

  • 401

    Missing or wrong x-replio-ghl-secret.

    Check the header.

Safe to repeat?

Upsert. Safe to repeat; the newest values win.

Voice

AI phone calls placed from a GHL workflow.

Place an AI call

POST/api/voice/dial

Places an outbound AI phone call to the lead through Retell. The outcome is written back to the GHL contact as tags and the AI Memory field when the call ends.

When to call it

From the snapshot workflows "3 - Voice: Confirm Call [Switch]" and "3 - Voice: Speed To Lead Call [Switch]", once voice is set up for the account.

Called by

GHL workflow Webhook action in the snapshot.

Auth: Per account secret

Header X-Replio-Webhook-Secret. In a GHL workflow use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E; GCC writes that custom value when the account is deployed. The secret only works for its own account.

Body fields

  • contact_idRequiredalso: contactId

    The GHL contact id.

  • location_idRequiredalso: locationId location.id

    The GHL sub-account id.

  • purposeRequired

    showup_confirm (confirm a booking) or stl_first_contact (speed to lead first call).

  • persona_name, company_name, timezone

    Who the caller is and the lead local zone.

  • script, first_message

    Override the opening line or script for this call.

  • set_id, tenant_id

    Optional.

Example request

curl
curl -X POST https://command.growthlio.com/api/voice/dial \
  -H "X-Replio-Webhook-Secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"contact_id":"{{contact.id}}","location_id":"t1xDJ87jWjC1gmCMpg04","purpose":"showup_confirm"}'

Success response (200)

JSON
{
  "status": "placed",
  "dial_id": "...",
  "call_id": "...",
  "callnow_removed": true
}

Errors and retrying

  • 400

    Body missing a required field or not JSON.

    Fix the webhook body. Not retryable as is.

  • 401

    Missing or wrong X-Replio-Webhook-Secret.

    Check the header uses the account custom value.

  • 403

    set_id does not belong to this location.

    Remove set_id or send the right one.

  • 503

    A lookup or write failed on our side.

    Retry 2 to 3 times, a few seconds apart.

  • 429

    Over 120 dials a minute.

    Back off and retry.

Safe to repeat?

One call per account, contact, purpose, appointment and day, with a per contact lock. A repeat returns status skipped, reason duplicate.

  • status skipped is a 200, not an error. reason says why (duplicate, outside calling hours, voice not set up and so on).

Ads outcomes

Tell Meta what really happened to a lead.

Qualified lead to Meta

POST/api/ads/ghl/qualified

A lead the account marked qualified is recorded and sent to Meta as a QualifiedLead conversion, so ads optimise toward real prospects instead of form fills.

When to call it

The "Ads: Qualified lead to Meta" workflow in the Full snapshot.

Called by

GHL workflow Webhook action in the snapshot.

Auth: Per account secret

Header X-Replio-Webhook-Secret. In a GHL workflow use Npk0zT8p5I9wEPt3BCmy0OkYBtA3v9aeSoGZG8SWO6E; GCC writes that custom value when the account is deployed. The secret only works for its own account.

Body fields

  • contact_idRequiredalso: contactId

    The GHL contact id.

  • location_idRequiredalso: locationId location.id

    The GHL sub-account id.

  • email, phone, first_name, last_name, fbc, fbp, value, currency

    Any filled field improves Meta match quality. Same field names as the outcomes endpoint below.

  • source, set_id, tenant_id

    Optional.

Example request

curl
curl -X POST https://command.growthlio.com/api/ads/ghl/qualified \
  -H "X-Replio-Webhook-Secret: YOUR_ACCOUNT_SECRET" \
  -H "content-type: application/json" \
  -d '{"contact_id":"{{contact.id}}","location_id":"t1xDJ87jWjC1gmCMpg04","email":"{{contact.email}}","phone":"{{contact.phone}}"}'

Success response (200)

JSON
{
  "ok": true,
  "duplicate": false
}

Errors and retrying

  • 400

    Body missing a required field or not JSON.

    Fix the webhook body. Not retryable as is.

  • 401

    Missing or wrong X-Replio-Webhook-Secret.

    Check the header uses the account custom value.

  • 403

    set_id does not belong to this location.

    Remove set_id or send the right one.

  • 503

    A lookup or write failed on our side.

    Retry 2 to 3 times, a few seconds apart.

Safe to repeat?

One qualified event per contact. A repeat is a 200 duplicate.

  • The stage is always qualified on this route. For other stages use the outcomes endpoint.
  • Returns 200 unmatched when the account has no ads client in GCC.

Lead outcome (any stage)

POST/api/ads/clients/{clientId}/outcomes

Tells Meta what really happened to a lead: qualified, contacted, booked, showed, closed or lost. Each becomes a conversion event Meta can optimise for.

When to call it

Any CRM stage change you want Meta to learn from. Works from GHL, n8n or any CRM that can send a webhook.

Called by

GHL workflow, n8n or another CRM.

Auth: Workspace API token

Header Authorization: Bearer <token>. Growthlio issues the workspace token (it starts envk_ and is shown once; only a hash is stored). clientId in the URL is the GCC ads client id, also given by Growthlio.

Body fields

  • stageRequiredalso: Stage event status

    lead, qualified, contacted, booked, showed, closed or lost.

  • external_idRequiredalso: contact_id contactId id lead_id_crm

    Your id for the lead. Used for dedupe.

  • lead_idalso: leadgen_id meta_lead_id

    Meta lead form id when the lead came from an instant form.

  • ad_id, adset_id, campaign_id

    Meta ids when you have them.

  • email, phone, first_name, last_name, city, state, zip, country

    Hashed by GCC before Meta sees them. More fields, better match.

  • fbc, fbp

    Meta click and browser ids from the landing page.

  • valuealso: amount revenue deal_value

    Money value, with currency.

  • occurred_atalso: timestamp event_time

    When it happened. Defaults to now.

Example request

curl
curl -X POST https://command.growthlio.com/api/ads/clients/{clientId}/outcomes \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "content-type: application/json" \
  -d '{"stage":"booked","external_id":"{{contact.id}}","email":"{{contact.email}}","phone":"{{contact.phone}}","value":450,"currency":"USD"}'

Success response (200)

JSON
{
  "ok": true,
  "duplicate": false
}

Errors and retrying

  • 400

    Unknown stage or missing external_id.

    Fix the body.

  • 401

    Missing or wrong bearer token.

    Check the token.

  • 404

    clientId not found in your workspace.

    Check the id in the URL.

  • 500

    Write failed.

    Retry.

Safe to repeat?

One event per client, external_id and stage. A repeat is a 200 with duplicate: true.

Social

Facebook and Instagram comments into the inbox.

Social comment in

POST/api/engage/in

Saves each Facebook or Instagram comment into the GCC comment inbox so the team (and the responder) can see and answer it in one place.

When to call it

The intake workflow of the GHL Comment Responder, on every new comment.

Called by

GHL workflow Webhook action.

Auth: Per account engage token

Header x-engage-token, value . Every auth failure gets the same 401 body.

Body fields

  • location_idRequiredalso: locationId location.id

    The GHL sub-account id.

  • page_idRequired

    The Facebook page id (or Instagram account id) the comment is on. Must be on file for the account.

  • platform

    facebook, instagram or other.

  • comment_id, ghl_comment_id, post_id

    Platform ids. Used for dedupe.

  • contact_id, commenter_name, comment, created_time, tags

    Who said what and when.

Example request

curl
curl -X POST https://command.growthlio.com/api/engage/in \
  -H "x-engage-token: YOUR_ENGAGE_TOKEN" \
  -H "content-type: application/json" \
  -d '{"location_id":"t1xDJ87jWjC1gmCMpg04","page_id":"1234567890","platform":"facebook","comment_id":"...","contact_id":"{{contact.id}}","commenter_name":"{{contact.name}}","comment":"..."}'

Success response (200)

JSON
{
  "status": "recorded"
}

Errors and retrying

  • 400

    missing_page_id.

    Send page_id.

  • 401

    Missing or wrong x-engage-token.

    Check the custom value.

  • 413

    Body too large.

    Trim the payload.

  • 429

    Rate limit (300 a minute per IP, plus per account).

    Back off and retry.

  • 503

    Write failed.

    Retry.

Safe to repeat?

One row per account and comment. A repeat returns status duplicate. A page not on file returns 200 unmatched.

Growthlio Command Centre. Secrets and tokens come from the Growthlio team privately. Questions or a new integration: talk to your Growthlio contact.