API փաստաթղթեր
Ձեր սեփական չատ ինտերֆեյսը՝ վեբ, iOS, Android կամ սերվերային, HighChat բոտի վրա։ Հանրային /v1 API՝ հոսքային պատասխաններ, զրույցի հիշողություն, օպերատորի փոխանցում, CSAT։
Base URL https://app.highchat.am
Սեփական ինտերֆեյս պետք չէ՞։ Կայքի համար կա պատրաստի վիջեթ՝ մեկ տող («Ալիքներ» → «Կայքի վիջեթ»).
<script src="https://app.highchat.am/widget/highchat.js"
data-key="ՁԵՐ-ԲԱՆԱԼԻՆ"
data-api="https://app.highchat.am" defer></script>
Այս էջը նրանց համար է, ովքեր կառուցում են իրենց ինտերֆեյսը։
Արագ մեկնարկ
Ստեղծեք բանալի «Ալիքներ» → «REST API» բաժնում, ապա ստացեք հոսքային պատասխան.
curl -N https://app.highchat.am/v1/chat \
-H "Authorization: Bearer hc_xxx" \
-H "Content-Type: application/json" \
-d '{"message":"Բարև, ի՞նչ ժամերի եք աշխատում"}'
Նույնականացում
Բոտը ճանաչվում է երկու եղանակներից մեկով։
Ա · API բանալիխորհուրդ է տրվում
Միացրեք «REST API»-ն (կամ «Կայքի վիջեթը») «Ալիքներ» էջում. բանալին ցուցադրվում է մեկ անգամ։ Ուղարկեք այն ամեն հարցումով.
Authorization: Bearer hc_xxxxxxxxxxxxxxxxxxxx
Բանալին հրապարակելի client credential է՝ կապված մեկ բոտի հետ, արագությամբ սահմանափակված, անվտանգ բրաուզերի bundle-ում կամ բջջային հավելվածում (ինչպես վիջեթի բանալին)։ Այն սերվերային գաղտնիք չէ. նրանով հնարավոր չէ կարդալ ձեր վահանակը, գիտելիքը կամ այլ բոտեր։ Ուզում եք բանալին ընդհանրապես չդնել client-ում. անցկացրեք հարցումները ձեր backend-ով (տես օրինակները)։
Բ · Հանրային slug (առանց բանալու)
Եթե միացրել եք բոտի «Հանրային զրույցի էջը» («Ալիքներ» էջում), կանչեք նույն endpoint-ները ?slug=your-slug պարամետրով՝ առանց header-ի։ Սա նույն մակերեսն է, որ սպասարկում է highchat.am/c/your-slug էջը։
Հաղորդագրություն ուղարկել — POST /v1/chat
Պատասխանը հոսում է հատված առ հատված՝ Server-Sent Events ձևաչափով։
POST https://app.highchat.am/v1/chat
Authorization: Bearer hc_xxx # կամ POST /v1/chat?slug=your-slug (առանց header-ի)
Content-Type: application/json
{
"message": "Բարև, ի՞նչ ժամերի եք աշխատում", // պարտադիր, 1–4000 նիշ
"conversation_id": "…", // ոչ պարտադիր — շարունակել զրույցը
"visitor_ref": "user-42", // ոչ պարտադիր — ձեր օգտատիրոջ ID, ≤200 նիշ
"customer_email": "…", // ոչ պարտադիր — միայն նոր զրույցի առաջին հաղորդագրության հետ
"customer_phone": "…" // ոչ պարտադիր — նույն կանոնով
}
customer_email-ը և customer_phone-ը գրանցվում են միայն նոր զրույց ստեղծելիս (վիջեթի նախազրույցի ձևի դաշտերն են) և հետագայում չեն փոխվում. էլ. հասցեով հաճախորդը փակված զրույցի պատճենն է ստանում, իսկ օպերատորը կարող է պատասխանել նամակով։
Իրադարձությունների հոսք
Պատասխանը text/event-stream է. ամեն իրադարձություն՝ event: <name> + data: <json>.
| event | data | ինչ անել |
|---|---|---|
meta | { conversation_id } | Գալիս է առաջինը։ Պահեք conversation_id-ն և ուղարկեք հաջորդ հարցումով՝ զրույցը շարունակելու համար։ |
delta | { text } | Պատասխանի հատված. ավելացրեք ընթացիկ հաղորդագրությանը։ |
done | { message_id, state, ts } | Պատասխանն ավարտված է։ state ∈ bot | pending_human | closed։ |
human | { message, ts } | Զրույցը մարդու մոտ է. բոտը չի պատասխանի։ Ցույց տվեք message-ը, ապա հարցրեք պարբերաբար։ |
error | { message } | Ցույց տվեք օգտատիրոջը. հայերեն, ցուցադրման համար անվտանգ տող է։ |
ping | {} | Keepalive։ Անտեսեք։ |
EventSource API-ն (միայն GET) չի աշխատի. կարդացեք response body-ն որպես հոսք, ինչպես օրինակներում։ Նոր զրույց սկսելու համար conversation_id մի ուղարկեք. շարունակելու համար ուղարկեք meta-ից ստացածը։Օպերատորի փոխանցում
Երբ բոտը փոխանցում է զրույցը (human իրադարձություն կամ done՝ state: pending_human), մարդը պատասխանում է վահանակի «Մուտքարկղ»-ից։ Պատասխանները ստանալու համար հարցրեք պարբերաբար.
GET https://app.highchat.am/v1/conversations/{id}/messages?after={ISO-ts}
Authorization: Bearer hc_xxx # կամ ?slug=your-slug&after=…
{
"state": "human",
"messages": [ { "id":"…", "role":"assistant", "content":"…", "created_at":"2026-06-14T10:00:00.000Z" } ],
"ts": "2026-06-14T10:00:05.000Z"
}
Վերադարձվում են միայն պատասխանող կողմի (role: assistant) հաղորդագրությունները։ Վերջին created_at-ը փոխանցեք որպես after՝ միայն նորերը ստանալու համար։ Հարցրեք ~4 վայրկյանը մեկ, քանի դեռ state-ը human/pending_human է. դադարեցրեք, երբ դառնում է bot կամ closed։
CSAT գնահատական — POST /v1/conversations/{id}/rating
POST https://app.highchat.am/v1/conversations/{id}/rating
Authorization: Bearer hc_xxx
Content-Type: application/json
{ "rating": 1, "comment": "շատ օգտակար էր" } // 1 = դրական, 0 = բացասական. comment-ը ոչ պարտադիր է, ≤1000 նիշ
Գնահատականները երևում են վահանակի «Վերլուծություն» էջում և որպես զտիչ՝ «Զրույցներ»-ում։
Սխալներ և սահմանափակումներ
Սխալի ձևը մեկն է. message-ը հայերեն, օգտատիրոջը ցուցադրելի տող է.
{ "error": { "code": "rate_limited", "message": "Չափից շատ հաղորդագրություն. փորձեք մեկ րոպեից" } }
| HTTP | code | նշանակություն |
|---|---|---|
| 400 | invalid_input | դատարկ կամ 4000 նիշից երկար հաղորդագրություն, անվավեր after կամ rating |
| 401 | unauthenticated | բանալին կամ slug-ը բացակայում է կամ անվավեր է, կամ հանրային էջն անջատված է |
| 403 | forbidden_origin | բանալու համար վահանակում նշված են թույլատրված origin-ներ. բրաուզերային հարցումը եկել է այլ կայքից |
| 404 | not_found | անհայտ զրույց |
| 429 | rate_limited | սահմանաչափը գերազանցված է. սպասեք և կրկնեք |
Սահմանաչափեր. 30 հաղորդագրություն/րոպե՝ մեկ բանալու (բոտի) հաշվով, 15/րոպե՝ մեկ IP-ից։ Հանրային չատի համար կարող է գործել նաև օրական առաստաղ մեկ բոտի հաշվով. գերազանցելիս նույն 429-ն է՝ հայերեն հաղորդագրությամբ։
CORS. /v1-ն ընդունում է ցանկացած origin. կարող եք կանչել ուղիղ բրաուզերից։ Cookie-ներ չեն օգտագործվում։
Օրինակներ
const API = "https://app.highchat.am";
const KEY = "hc_xxx"; // կամ օգտագործեք ?slug=… և հանեք header-ը
let conversationId = null;
async function send(message, onDelta) {
const res = await fetch(`${API}/v1/chat`, {
method: "POST",
headers: { "content-type": "application/json", authorization: "Bearer " + KEY },
body: JSON.stringify({ conversation_id: conversationId, message }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = "", event = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const lines = buf.split("\n"); buf = lines.pop();
for (const line of lines) {
if (line.startsWith("event:")) event = line.slice(6).trim();
else if (line.startsWith("data:")) {
const data = JSON.parse(line.slice(5).trim());
if (event === "meta") conversationId = data.conversation_id;
else if (event === "delta") onDelta(data.text);
else if (event === "error") console.error(data.message);
}
}
}
}
function useHighChat(apiKey) {
const convId = useRef(null);
const [reply, setReply] = useState("");
async function send(message) {
setReply("");
const res = await fetch("https://app.highchat.am/v1/chat", {
method: "POST",
headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
body: JSON.stringify({ conversation_id: convId.current, message }),
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = "", ev = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
const lines = buf.split("\n"); buf = lines.pop();
for (const l of lines) {
if (l.startsWith("event:")) ev = l.slice(6).trim();
else if (l.startsWith("data:")) {
const d = JSON.parse(l.slice(5).trim());
if (ev === "meta") convId.current = d.conversation_id;
else if (ev === "delta") setReply((r) => r + d.text);
}
}
}
}
return { reply, send };
}
func send(_ message: String) async throws {
var req = URLRequest(url: URL(string: "https://app.highchat.am/v1/chat")!)
req.httpMethod = "POST"
req.setValue("Bearer hc_xxx", forHTTPHeaderField: "Authorization")
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try JSONSerialization.data(withJSONObject: [
"message": message, "conversation_id": conversationId as Any
])
let (bytes, _) = try await URLSession.shared.bytes(for: req)
var event = ""
for try await line in bytes.lines {
if line.hasPrefix("event:") { event = String(line.dropFirst(6)).trimmingCharacters(in: .whitespaces) }
else if line.hasPrefix("data:") {
let json = String(line.dropFirst(5)).trimmingCharacters(in: .whitespaces)
let d = (try? JSONSerialization.jsonObject(with: Data(json.utf8))) as? [String: Any] ?? [:]
if event == "meta" { conversationId = d["conversation_id"] as? String }
else if event == "delta" { appendToBubble(d["text"] as? String ?? "") }
}
}
}
val body = """{"message":${JSONObject.quote(message)},"conversation_id":${
conversationId?.let { JSONObject.quote(it) } ?: "null"}}"""
val req = Request.Builder()
.url("https://app.highchat.am/v1/chat")
.addHeader("Authorization", "Bearer hc_xxx")
.post(body.toRequestBody("application/json".toMediaType()))
.build()
client.newCall(req).execute().use { res ->
val src = res.body!!.source()
var event = ""
while (!src.exhausted()) {
val line = src.readUtf8Line() ?: break
when {
line.startsWith("event:") -> event = line.substring(6).trim()
line.startsWith("data:") -> {
val d = JSONObject(line.substring(5).trim())
when (event) {
"meta" -> conversationId = d.optString("conversation_id")
"delta" -> appendToBubble(d.optString("text"))
}
}
}
}
}
// Բանալին պահեք ՁԵՐ սերվերում. հավելվածը խոսում է ձեր backend-ի հետ։
app.post("/chat", async (req, res) => {
const upstream = await fetch("https://app.highchat.am/v1/chat", {
method: "POST",
headers: { "content-type": "application/json",
authorization: `Bearer ${process.env.HIGHCHAT_KEY}` },
body: JSON.stringify(req.body),
});
res.setHeader("content-type", "text/event-stream");
upstream.body.pipe(res); // SSE-ն անցնում է ուղիղ հոսքով
});
Zendesk
HighChat-ը Zendesk-ի հետ աշխատում է երկու տարբերակով։
Ա · Առանց Zendesk-իամենապարզը
Փոքր թիմերի մեծ մասին HighChat-ը բավական է որպես աջակցման ամբողջ համակարգ. բոտը պատասխանում է կայքում, Telegram-ում, WhatsApp-ում, Instagram-ում և Messenger-ում, իսկ այն, ինչ չի կարողանում լուծել, ընկնում է ներկառուցված «Մուտքարկղ», որտեղ մարդը շարունակում է նույն զրույցը։
Բ · Պահեք Zendesk-ը. HighChat-ը պատասխանում է առաջինը
REST API-ն ադապտերն է. Zendesk-ի webhook-ների և POST /v1/chat-ի միջև դրեք ~40 տող middleware։ Բոտը փակում է կրկնվող հարցերը. մնացած զրույցներն անփոփոխ հասնում են ձեր Zendesk օպերատորներին։
// 1. Zendesk Admin Center → Apps & integrations → Webhooks:
// subscribe to "Comment created" (or a Messaging/Sunshine webhook)
// pointing at your middleware URL.
// 2. The middleware forwards the visitor's text to HighChat and
// posts the reply back through the Zendesk API.
app.post("/zendesk-hook", async (req, res) => {
res.sendStatus(200); // ack fast; Zendesk retries otherwise
const { ticket_id, comment, author_is_end_user } = req.body;
if (!author_is_end_user) return; // ignore agent/bot echoes
const r = await fetch("https://app.highchat.am/v1/chat", {
method: "POST",
headers: { "content-type": "application/json",
authorization: `Bearer ${process.env.HIGHCHAT_KEY}` },
body: JSON.stringify({
message: comment,
conversation_id: store.get(ticket_id), // keep 1 ticket = 1 conversation
}),
});
const reply = await readSse(r); // collect `delta` events → full text
store.set(ticket_id, reply.conversation_id);
if (reply.escalated) return; // bot bowed out → leave it to agents
await fetch(`https://YOURDOMAIN.zendesk.com/api/v2/tickets/${ticket_id}`, {
method: "PUT",
headers: { "content-type": "application/json",
authorization: "Basic " + zendeskAuth },
body: JSON.stringify({ ticket: { comment: { body: reply.text, public: true } } }),
});
});
Կարևոր է արտադրական միջավայրում.
- Մեկ Zendesk ticket = մեկ HighChat զրույց. պահեք առաջին պատասխանի
conversation_id-ն, որ բոտը պահի համատեքստը։ - Երբ SSE հոսքն ավարտվում է
humanիրադարձությամբ, բոտը փոխանցել է զրույցը. դադարեցրեք ավտոմատ պատասխանները և թողեք ticket-ը ձեր օպերատորներին։ - Խուսափեք օղակներից. փոխանցեք միայն վերջնական օգտատերերի գրածը, երբեք՝ ձեր բոտի պատասխանները։
Հակիրճ տեղեկատու
| Endpoint | Auth | նշանակություն |
|---|---|---|
POST /v1/chat | բանալի կամ ?slug= | հաղորդագրություն ուղարկել, պատասխանը՝ SSE հոսքով |
GET /v1/conversations/{id}/messages?after= | բանալի կամ ?slug= | օպերատորի պատասխանների հարցում |
POST /v1/conversations/{id}/rating | բանալի կամ ?slug= | CSAT գնահատական (1/0) |
GET /v1/p/{slug} | առանց auth | հանրային էջի bootstrap՝ {name, greeting, suggestions} |