Skip to content
Documentation

SDKs and sample code

ClickFix SDK examples for Go, Node.js, PHP and React; authentication, retries, pagination, webhook signatures and requests without an SDK.

ClickFix has server SDKs for Go, Node.js and PHP and a UI-free widget client for React. The SDKs handle authentication, bounded retries (including Retry-After), pagination and webhook signature verification.

Packages

npm install @getclickfix/node (0.1.0, Node 22+), npm install @getclickfix/react-widget (0.1.0, React 18+), composer require clickfix/sdk (0.1.0, PHP 8.1+) and go get github.com/ClickSolveMainTR/clickfix-sdks/go@v0.1.0. The end-to-end setup order with verified samples at every step: Integration guide. For Laravel 10-13, composer require clickfix/laravel (0.1.0): Laravel package. The examples without an SDK below do the same work over plain HTTP; every endpoint in the API reference also ships ready requests for curl, JavaScript, Python and Go.

Use https://api.getclickfix.com as the base URL (without /v1). Keep API keys on your server; never put them in browser code. Creating keys and scopes: API.

Create a client and send a request

For writes, pass a stable Idempotency-Key that identifies the operation (e.g. an order number) and send the same value when retrying.

import { createClient } from "@getclickfix/node";

const client = createClient({
  baseURL: "https://api.getclickfix.com",
  apiKey: process.env.CLICKFIX_API_KEY!,
});

const created = await client.request(
  "/v1/public-api/conversations",
  "post",
  {
    contact_uuid: contactUUID,
    inbox_uuid: inboxUUID,
    subject: "Delivery question",
  },
  { idempotencyKey: "order-42-support", signal: AbortSignal.timeout(30_000) },
);

On error responses the Node.js client throws an APIError with the status, code, request ID and details. GET requests and keyed writes are tried at most three times by default (exponential backoff, bounded Retry-After); unkeyed writes are never retried. The Go client adds a 30-second HTTP timeout and the same retry rule.

The API stores completed write responses, including errors: retrying with the same key cannot turn a stored failure into success. Use a new key for corrected input.

Pagination

import { paginate } from "@getclickfix/node";

for await (const contact of paginate(async (limit, offset) => {
  const envelope = await client.request(
    "/v1/public-api/contacts",
    "get",
    undefined,
    {
      query: { limit, offset },
    },
  );
  // Validate envelope.data with your own schema and return {items, total, limit, offset}.
  return validateContactPage(envelope.data);
})) {
  await consume(contact);
}

Offset pagination is for snapshots; concurrent inserts can shift pages. Deduplicate UUIDs when syncing, and use the trigger cursor for incremental sync.

Webhook signature and identity hash

import { identityHash, verifyWebhook } from "@getclickfix/node";

const valid = verifyWebhook(
  secret,
  headers.timestamp,
  headers.signature,
  rawBody,
);

// user_hash for a signed-in user of the web widget
const userHash = identityHash(identitySecret, authenticatedUser.id);

Verify the signature on the raw body before parsing JSON, and store and deduplicate ClickFix-Webhook-Id before any side effects. Verification without an SDK (Node.js, Python, Go): Webhooks.

React widget client

Typed hooks to drive the ready-made messenger (bubble, home, help center) from your own button:

"use client";
import { useMemo } from "react";
import { useClickFix, useClickFixEvent } from "@getclickfix/react-widget";

export function Support({ consent }: { consent: boolean }) {
  const options = useMemo(
    () =>
      consent
        ? {
            appOrigin: "https://getclickfix.com",
            widgetId: "WIDGET_ID",
            hideLauncher: true,
          }
        : null, // null delays loading until consent
    [consent],
  );
  const { ready, unread, open, sdk } = useClickFix(options);
  useClickFixEvent("conversation:ended", (e) => console.log(e.status));

  return (
    <button aria-expanded={open} disabled={!ready} onClick={() => sdk.toggle()}>
      Support {unread || ""}
    </button>
  );
}

With the UI-free client you render conversations and messages yourself:

await client.identify({ user_id: user.id, user_hash: serverProvidedHmac });
const { items } = await client.conversations();
await client.createConversation({
  text: "Hello",
  client_id: crypto.randomUUID(),
});
await client.sendMessage(
  conversationUUID,
  { text: "Photo", client_id: stableMessageUUID },
  file,
);
await client.markRead(conversationUUID);

user_hash is produced on your server; never send the identity secret to the browser. Widget commands and events: Web widget.

Requests without an SDK

curl -X POST "https://api.getclickfix.com/v1/public-api/conversations/CONVERSATION_UUID/messages" \
  -H "Authorization: Bearer $CLICKFIX_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042-note" \
  -d '{ "content": "The order has shipped.", "private": true }'

On 429 RATE_LIMITED, wait for Retry-After and retry with the same Idempotency-Key; don't retry 400, 401 and 403.

Next steps