Hand this to Cursor, Claude Code, or Windsurf
Paste one URL and your AI coder fetches the entire integration spec — endpoints, webhook, HMAC, SDK. No PDF, no login.
You are integrating Znatoka (contact-safe buyer<->seller dispute chat) into my app. Read the full API + webhook spec here: https://znatoka.com/llms.txt OpenAPI 3.1 JSON: https://znatoka.com/openapi.json Official SDK: npm package "chatdisiniaja" (Node/TypeScript). Requirements: - Keep CHATDISINIAJA_API_KEY server-side only (never expose to the browser). - When a dispute/refund starts, POST /api/v1/conversations to get an id plus buyer_url/seller_url (or the ready buyer_embed/seller_embed <script> snippets). - Deliver the correct link/snippet to each party. - Webhook is OPTIONAL: the embed/SSE chat works without it. Add one only to notify a party who isn't viewing the chat — verify the X-Chatdisiniaja-Signature header (HMAC-SHA256 over the raw body) and notify notify_role via open_conversation_url. Otherwise rely on the embed widget or poll GET /api/v1/conversations/:id. - Close the conversation when the dispute is resolved. Follow the spec exactly. Do not invent endpoints or fields.
Easiest way — embed widget
No chat UI to build. Create a conversation (step 1) to get a token for each party, then paste this single lineon your platform's page — a floating chat bubble appears (like Tawk.to), with no contact leaking:
<script src="https://znatoka.com/embed.js"
data-token="<this_party_token>" async></script>The token is in buyer_url/seller_url (the part after /c/), or use the buyer_embed/seller_embedfield from the create response directly — it's already a ready-to-paste snippet. Options: data-position="left|right", data-color="#5b7cfa", data-label="Help", data-fullscreen="mobile|always|off" (default mobile — opens full-screen on phones so nothing looks cramped).
Support widget + unread badge
embed.jsabove needs a token per party — it suits buyer↔seller disputes you create from your backend. For plain visitor → support chat on your own site, use widget.js instead: paste one line on every page, no backend call, no token to manage.
<script src="https://znatoka.com/widget.js"
data-site="<your widget_key>" async></script>Your widget_key (wk_...) is public — it is safe in page source, unlike your api_key. The widget gives each visitor a random id in localStorage and keeps one open conversation per visitor, so a returning visitor lands back in the same thread. Read and reply to everything in the admin inbox.
Unread badge
When you reply and the visitor has the panel closed, a red count appears on the bubble — no setup needed, it is on by default. It works because the widget loads the chat hidden as soon as the page does, so its realtime stream is already connected before anyone clicks the bubble. The count clears the moment the visitor opens the panel, and is remembered per browser, so a page reload will not re-announce messages they already read.
If the tab is in the background, the page title also blinks (1) Pesan baru until the visitor comes back. It never blinks while they are looking at the page, and the original title is always restored. Turn it off with data-title-alert="off".
Options
<script src="https://znatoka.com/widget.js"
data-site="wk_..."
data-label="Help" <!-- bubble tooltip + screen-reader name -->
data-position="left" <!-- left | right (default right) -->
data-color="#5b7cfa" <!-- bubble gradient start -->
data-fullscreen="mobile" <!-- mobile | always | off -->
data-title-alert="on" <!-- on | off: blink the tab title -->
async></script>Everything renders in a shadow root, so your CSS and the widget's cannot affect each other. data-fullscreen="mobile" (the default) opens full-screen on phones so nothing looks cramped. The bubble is a real <button> whose accessible name carries the unread count, and the count is announced politely to screen readers.
Brand colors & chat theme
Make each chat look like your brand: colors, font and bubble shape of the chat page (/c/:token), the embed and the support widget — rendered server-side, so visitors see your colors from the first pixel. Every chat link can carry its own colors, so several brands on one account just use different parameters.
/c/<token>?primary=16a34a&mode=light&font=poppins. With embed.js or widget.js, put the same values on the script tag as data-primary, data-mode, … — they are forwarded for you. Colors are hex with or without #; an invalid value is simply ignored.Order of precedence: Znatoka default ← account theme (/api/v1/theme) ← conversation theme ← URL parameters / data-* attributes.
1 · Chat link — just add these parameters (no API needed)
https://znatoka.com/c/<token>?mode=light&primary=e11d48&on_primary=ffffff&background=ffffff&header=fff1f2&text=1f2937&muted=6b7280&border=fecdd3&bubble_other=f3f4f6&bubble_other_text=111827&radius=18&font=poppins
2 · Embed / widget — same values as data-* attributes (also works on widget.js)
<script src="https://znatoka.com/embed.js" data-token="<token>"
data-mode="light"
data-primary="e11d48"
data-on-primary="ffffff"
data-background="ffffff"
data-header="fff1f2"
data-text="1f2937"
data-muted="6b7280"
data-border="fecdd3"
data-bubble-other="f3f4f6"
data-bubble-other-text="111827"
data-radius="18"
data-font="poppins" async></script>3 · Optional: make it the default for every chat via API
curl -X PATCH https://znatoka.com/api/v1/theme \
-H "X-Api-Key: <your api_key>" \
-H "Content-Type: application/json" \
-d '{"mode":"light","primary":"#e11d48","on_primary":"#ffffff","background":"#ffffff","header":"#fff1f2","text":"#1f2937","muted":"#6b7280","border":"#fecdd3","bubble_other":"#f3f4f6","bubble_other_text":"#111827","radius":18,"font":"poppins"}'Per-brand / per-conversation
POST https://znatoka.com/api/v1/conversations
X-Api-Key: <tenant api_key>
Content-Type: application/json
{
"external_ref": "ORDER-77",
"buyer": { "name": "Buyer #77" },
"seller": { "name": "Shop B" },
"theme": { "primary": "#16a34a", "mode": "light" }
}# change one conversation later (only the fields you send are overridden)
PUT https://znatoka.com/api/v1/conversations/<id>/theme
{ "primary": "#0ea5e9", "bubble_other": "#e0f2fe" }
# read the current theme + the list of accepted fields
GET https://znatoka.com/api/v1/theme
# back to the default look
DELETE https://znatoka.com/api/v1/theme
# bad value -> 422
{ "error": "invalid_theme", "fields": { "primary": "must be a hex color like #1a73e8" } }Fields
| Field | Type | What it colors |
|---|---|---|
mode | "dark" | "light" | "auto" | Base palette. "auto" follows the visitor’s system setting. |
primary | hex color | Brand color: buttons, links, the visitor’s own bubbles, widget launcher. |
on_primary | hex color | Text/icon color on top of primary. Default #ffffff. |
background | hex color | Chat page background. |
header | hex color | Top bar & composer background. |
text | hex color | Main text color. |
muted | hex color | Secondary text (timestamps, hints). |
border | hex color | Dividers and bubble borders. |
bubble_self | hex color | Bubbles sent by the person viewing the chat. Default = primary. |
bubble_self_text | hex color | Text inside bubble_self. Default = on_primary. |
bubble_other | hex color | Bubbles from the other side (your support team, for visitors). |
bubble_other_text | hex color | Text inside bubble_other. |
radius | number 0–28 | Bubble corner radius in px. Default 15. |
font | "system" | "inter" | "roboto" | "poppins" | "nunito" | "serif" | "mono" | Font family. |
All fields are optional. Colors must be hex (#rgb, #rrggbb, #rrggbbaa) — other CSS values are rejected so a theme can never inject styles into the page. The widget launcher uses primary unless you set data-color on the script tag.
Official SDK (Node/TypeScript)
The chatdisiniaja package wraps the REST API + HMAC webhook verification + embed helpers, fully typed.
npm install chatdisiniaja
import { Chatdisiniaja } from 'chatdisiniaja';
const cdj = new Chatdisiniaja({ apiKey: process.env.CHATDISINIAJA_API_KEY! });
const conv = await cdj.createConversation({
externalRef: 'REFUND-123',
buyer: { name: 'Buyer #4821' },
seller: { name: 'Seller (anonymous)' },
});
// paste conv.buyer_embed / conv.seller_embed on each party's pageAuthentication
All /api/v1/* endpoints require the header X-Api-Key: <api_key>. Participant endpoints /c/:token/* are opened directly by the buyer/seller via a secret link — no API key. Keep your api key server-side only.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/conversations | Create a conversation and return buyer_url & seller_url. |
| GET | /api/v1/conversations/:id | Conversation detail + full transcript. |
| POST | /api/v1/conversations/:id/messages | Send a message as system/role. notify:false to mirror without a webhook. |
| POST | /api/v1/conversations/:id/close | Close the conversation. |
| GET · PUT · PATCH · DELETE | /api/v1/theme | Brand colors for every chat of your account. See Brand colors. |
| PUT · DELETE | /api/v1/conversations/:id/theme | Brand colors for one conversation (overrides the account theme). |
| GET | /c/:token | Participant chat UI (HTML). |
| GET | /c/:token/info | Conversation info + the other party's label. |
| GET | /c/:token/messages | List messages. |
| POST | /c/:token/messages | Participant sends a message. |
| GET | /c/:token/stream | Realtime SSE. |
| GET | /widget.js | Support-widget loader. Public, no key — takes data-site=<widget_key>. |
| POST | /w/:widgetKey/session | Public. { visitor_id } → { token }: opens or restores that visitor's conversation. |
1. Create a conversation
buyer.name/seller.name is the label the other party sees — anonymize here.
POST https://znatoka.com/api/v1/conversations
X-Api-Key: <tenant api_key>
Content-Type: application/json
{
"external_ref": "REFUND-PR05001",
"subject": "Refund: Gmail can't log in",
"buyer": { "name": "Buyer #4821" },
"seller": { "name": "Seller (anonymous)" }
}
201
{ "id": "...", "buyer_url": "https://znatoka.com/c/...", "seller_url": "https://znatoka.com/c/..." }2. Webhook on incoming message optional
GET /api/v1/conversations/:id.To enable it, set the tenant webhook_url in the Portal. On each incoming message we POST the event below; use open_conversation_url to notify notify_role.
POST <your tenant webhook_url>
X-Chatdisiniaja-Signature: sha256=<HMAC-SHA256(body, webhook_secret)>
{
"event": "message.created",
"conversation_id": "...", "external_ref": "REFUND-PR05001",
"sender_role": "buyer",
"preview": "Hi, the account won't log in...",
"notify_role": "seller",
"open_conversation_url": "https://znatoka.com/c/<token>"
}3. Verify the webhook signature (Node.js)
Only needed if you use webhooks. Compute the HMAC over the raw request body, before JSON parsing.
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const sig = (header || '').replace(/^sha256=/, '');
const exp = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return sig.length === exp.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(exp));
}