Harnix
Integrate

Server-to-server

Call /v2/chat directly with an API key from your own backend.

For when you don't need the widget — your backend sends messages on behalf of its own end-users directly, e.g. to plug into an internal chat system, a Slack bot, or an automated ticket pipeline. No token exchange needed: call POST /v2/chat directly with an API key (Console → API keys & Members → New API key — shown exactly once, at creation).

An API key never reaches the browser

An API key is a secret that grants access to the whole chat endpoint for your organization. Call it only from your backend — never from browser-side JavaScript, never committed into frontend code.

Request

POST https://api.harnix.vn/v2/chat
Content-Type: application/json
X-API-Key: sk_xxxxxxxxxxxxxxxx
Idempotency-Key: 2f6a...   (recommended, see below)
{
  "agent_id": "b6b0f9b2-...",
  "content": "What's our refund policy?",
  "conversation_id": null,
  "end_user_id": "user_42"
}
  • agent_id — required, must be an active agent.
  • content — required, 1–32,000 characters.
  • conversation_id — leave it out to start a new conversation; pass one in to continue an existing conversation (must match agent_id, otherwise you get validation_error).
  • end_user_id — given → the conversation belongs to that end-user (upserts an identified end-user with this value as external_id); omitted → the conversation belongs to a service end-user that stands in for the API key itself — every request that skips end_user_id shares this one "person".

Response

202 Accepted — there's no answer yet, the run is still going:

{
  "run_id": "0e2b...",
  "message_id": "9ac1...",
  "conversation_id": "77fd..."
}

Get the answer by streaming this run, or by reading the conversation back with GET /v2/conversations/{id} once the run finishes.

Idempotency

Send an Idempotency-Key header (255 characters max) so a retry after a network timeout is safe — the same key on the same conversation + end-user returns the message already created, never a second one. Details on Idempotency & cancel.

Confirming an action

When the agent wants to call a connector tool that requires confirmation, the run pauses instead of answering: its stream emits an approval frame, and GET /v2/conversations/{id} shows the same request under pending_approval. Your backend decides with the same API key:

POST https://api.harnix.vn/v2/runs/{run_id}/approval
X-API-Key: sk_xxxxxxxxxxxxxxxx
Content-Type: application/json

{ "outcome": "approved" }

outcome is "approved" or "denied"; 202 returns the run, which resumes right away. Over an API key there is no remember — sending it returns 422 validation_error. While a conversation has a run waiting, POST /v2/chat with that conversation_id returns 409 approval_pending; decide first, then send. The rules in full are on Idempotency & cancel.

Answering questions (ask_user)

When the agent calls the built-in ask_user tool to ask the user questions, the run pauses in waiting_input instead of answering: its stream emits a question frame, and GET /v2/conversations/{id} reports the questions under pending_question. Your backend can reply in two ways:

  1. Call POST /v2/runs/{run_id}/answer with an API key:
POST https://api.harnix.vn/v2/runs/{run_id}/answer
X-API-Key: sk_xxxxxxxxxxxxxxxx
Content-Type: application/json

{
  "answers": [
    { "selected": ["District 3"], "other": null }
  ]
}

Returns 202 Accepted with run details, and the run resumes immediately on the stream.

  1. Send a follow-up message via POST /v2/chat:

When a conversation has a run waiting for questions (waiting_input), calling POST /v2/chat with that conversation_id is treated as the user's free-text reply. The system returns 202 Accepted with the same waiting run (no new run created, not blocked by concurrent run limits), appends the message to the conversation, and resumes processing the run. Retrying with the same Idempotency-Key returns the same run without duplicate messages. The rules in full are on Idempotency & cancel.

Rate limits & budgets

Each API key has its own rate ceiling (300 requests/minute by default, adjustable by the operator) — exceeding it returns rate_limited with a retry_after. An end-user (including the service end-user) can have at most 3 runs in flight at once — exceeding that returns rate_limited with scope: "active_runs". If the organization or agent has a daily token budget, running out returns budget_exceeded — see Limits.

Next

  • Streaming — follow a run in real time.
  • Conversations — list and read history back.
  • Errors — the error shape and every error code.
  • Runs & traces — an older run's step-by-step detail is removed per your organization's retention setting; messages and usage figures are not.

On this page