Skip to content

Overview

The Kotally machine API lets external automations grant, check, deduct, and restore credits and read contact entitlement data over plain HTTP — with or without GoHighLevel. The API Quickstart walks through your first calls; this reference documents every endpoint.

  • Base URL: https://app.<your-domain>/api/v2
  • Auth: send Authorization: Bearer ktly_<token> on every request. Tokens are created under Admin → API Clients, are scoped to a single location, and carry a set of scopes (see below). The token format is ktly_<uuid>_<uuid> and is shown once at creation.
  • Format: requests and responses are JSON.
  • Outcome model: business outcomes return HTTP 200 with ok: true|false and a reason_code — an ineligible check or an insufficient balance is a 200 with ok: false, not an HTTP error. Reserve HTTP 4xx/5xx for transport-level problems (auth, validation, rate limits).

Every endpoint takes the same headers:

HeaderRequiredValue
AuthorizationyesBearer ktly_<token>
Content-Typeyes (for POST)application/json

Each API client is granted a subset of scopes. Grant only what an automation needs.

ScopeGrants access to
grantPOST /api/v2/grants
checkPOST /api/v2/entitlements/check-eligibility
deductPOST /api/v2/entitlements/deduct
restorePOST /api/v2/entitlements/restore
summaryAll GET /api/v2/contacts/… read endpoints

The state-changing endpoints — grants, deduct, and restore — require a request_id field.

  • Choose a stable, unique string per logical operation (e.g. your payment id for a grant, or your booking id suffixed with -deduct).
  • On a timeout or network error, resend the exact same payload with the same request_id — Kotally returns the original result without repeating the operation.
  • If the same request_id arrives while the first request is still processing, Kotally returns REQUEST_IN_PROGRESS with HTTP 409; wait a moment and retry.

Do not reuse a request_id across different operations or contacts.

List endpoints (the contact ledger, payments, and appointments reads) accept an optional ?limit= query parameter. The default is 20 and the maximum is 100.

Every response includes ok and reason_code; mutating endpoints also include a unique correlation_id — include it in support requests.

{
"ok": false,
"reason_code": "INSUFFICIENT_CREDITS",
"correlation_id": "a1b2c3d4-...",
"message": "Optional human-readable detail"
}
StatusWhen
200Business-logic outcome (including ineligible/insufficient — check ok)
400Validation error (missing required field, wrong type)
401Missing or invalid bearer token, insufficient scope, or wrong location
409REQUEST_IN_PROGRESS — a duplicate in-flight request
429RATE_LIMITED — slow down and retry
reason_codeMeaning
UNAUTHORIZEDMissing/invalid token, insufficient scope, or wrong location
RATE_LIMITEDToo many requests from this API client
VALIDATION_ERRORA required field is missing or has an invalid value
NO_ENTITLEMENTThe contact has no entitlement matching the product config or calendar
INSUFFICIENT_CREDITSThe contact’s entitlement does not have enough credits
CANCELLATION_WINDOW_EXPIREDA restore was rejected — the appointment is past the cancellation window
BILLING_SUSPENDEDWorkspace billing is suspended; credit mutations are paused
duplicate_payment_eventA grant was skipped — the external_payment_id was already processed
REQUEST_IN_PROGRESSThe same request_id is being processed by a concurrent request
NOT_FOUNDThe requested contact does not exist for this location