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.