Aiva over API

Drive the same Aiva agent that powers the chat widget from your own product, over HTTP.

Aiva over API lets your backend send end-user messages to Aiva and receive her replies, so you can build Aiva into surfaces we don't render: a mobile app, an in-product help screen, another chat system.

It is the same agent, not a separate product. A message you POST runs the full pipeline of your workspace: knowledge base, tools, identity gate, escalation to your team. The conversation is persisted and appears in your dashboard inbox exactly like a widget conversation, and a human agent can take over at any point.

The model is asynchronous: you POST a message, Aiva processes it, and the reply reaches you through a signed callback to your server or by polling the conversation. There is no blocking "wait for the answer" endpoint.

1. Get access

Aiva over API is invite-only during the beta. In the dashboard, open Channels → Aiva over API and submit the request form (what you are building, expected volume). We review requests quickly; you get an email when your workspace is approved, along with a small starter credit so you can make your first calls without setting up billing.

The rest of this guide assumes your workspace shows Active on that page.

2. Create an API key

In Channels → Aiva over API → Keys, create a key:

  • Scopes pick what the key can call. Grant only what the integration needs.
  • Callback URL (optional) is where we POST signed events: Aiva's replies, human agent replies, identity confirmations, handoff notices. Leave it empty to use polling instead.

The full key (ak_<id>_<secret>) and, if you set a callback URL, the callback signing secret are shown exactly once. Store both in your secret manager; neither is retrievable again.

3. Send a message

curl -X POST https://assistify.chat/api/v1/messages \
  -H "Authorization: Bearer ak_xxx_yyy" \
  -H "Content-Type: application/json" \
  -d '{
    "externalConversationId": "order-4821",
    "externalUserId": "user-93",
    "content": "What is your refund policy?",
    "idempotencyKey": "'"$(uuidgen)"'"
  }'
  • externalConversationId is your stable conversation id. The first message with a new id creates the conversation; later messages with the same id continue it.
  • externalUserId (optional) is your stable end-user id. It links messages to one contact and enables identity verification and per-user spend caps.
  • idempotencyKey is a UUID you generate per logical message. Retrying with the same key can never create a duplicate or double-charge; use a fresh UUID for each new message.

The response is 202 Accepted:

{ "status": "success", "data": {
  "conversationId": "…",   // our internal id
  "messageId": "…",
  "status": "queued"       // "queued" | "duplicate" | "skipped"
}}

queued means Aiva is generating a reply. duplicate is an idempotency replay of an earlier submit. skipped means the message was stored but no AI reply will be generated (for example a human agent owns the conversation); a reason field says why.

4. Receive the reply

Polling

Without a callback URL, poll the conversation until a new AI message appears:

curl https://assistify.chat/api/v1/conversations/order-4821 \
  -H "Authorization: Bearer ak_xxx_yyy"

The response contains the ordered message history with senderType per message (VISITOR, AI, AGENT). Poll every few seconds; replies typically land well under half a minute.

Callbacks

With a callback URL on the key, we POST each event to your endpoint:

// ai.reply
{
  "event": "ai.reply",
  "conversationId": "order-4821",   // your externalConversationId
  "messageId": "…",
  "seq": 2,
  "content": "Our refund policy is…",
  "visibility": "public",
  "poweredBy": "Assistify",
  "timestamp": "2026-07-02T10:30:00.000Z"
}

Respond with any 2xx quickly (we time out after 5 seconds). Failed deliveries are retried with exponential backoff. Deliveries carry a stable X-Assistify-Delivery-Id header that is reused across retries, so deduplicate on it.

visibility is "public" or "private". A private reply may reference personal or account data surfaced by a tool for a verified user; render it only in a surface where that user is authenticated.

Verify the signature

Every callback is signed with the key's callback secret: HMAC-SHA256 over `${timestamp}.${rawBody}`, sent as X-Assistify-Signature: sha256=<hex> with X-Assistify-Timestamp (unix seconds). Verify before trusting the payload:

const { createHmac, timingSafeEqual } = require('crypto');
 
function verify(req, secret) {
  const ts = req.headers['x-assistify-timestamp'];
  const sig = req.headers['x-assistify-signature']; // "sha256=…"
  const expected = 'sha256=' + createHmac('sha256', secret)
    .update(`${ts}.${req.rawBody}`).digest('hex');
  const a = Buffer.from(sig), b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Sign over the raw request body exactly as received. Parsing and re-serializing the JSON changes the bytes and breaks the signature.

Test your endpoint

In Keys, every key with a callback URL has a Send test action. It delivers a signed test.ping event through the same pipeline as real events, so you can confirm your endpoint and signature verification before going live. The outcome appears in the Logs tab.

5. When a human takes over

If Aiva escalates the conversation to your team, your callback receives conversation.handoff with the reason and a summary. From that point, replies typed by your agents in the dashboard inbox reach you as agent.reply events with the same shape as ai.reply, plus an agentName field. Handle both events through one code path and your end-user gets a seamless human handover.

Messages you POST while a human owns the conversation are stored and shown to the agent, and come back as status: "skipped" (no AI reply is generated).

6. Identity

The identity model mirrors the widget:

TierMeaning
anonymousNo externalUserId; each message uses an anonymous contact.
unverifiedYou supplied an externalUserId. Links messages to one contact, but does not unlock personal tools.
verifiedThe end-user completed a verification ceremony. Personal and account-scoped tools unlock.

Supplying an externalUserId is an assertion by your key, not proof. To verify, call POST /v1/identity/verify with method: "magic_link" and the user's email; the user clicks the emailed link, and your callback receives identity.verified. Verification is always earned by the end-user out-of-band; the API cannot assert it directly.

7. Billing

API usage is billed from a prepaid credit wallet, separate from your plan subscription. Pricing is per token consumed, at the rate shown in Channels → Aiva over API → Credits.

  • Before generating, the wallet must cover the worst case of one generation; otherwise the call fails with 402 INSUFFICIENT_CREDITS and nothing is charged.
  • After generating, the actual token cost is settled once. Idempotent retries never double-charge.
  • Top up manually, enable auto-recharge, or rely on the credits included with your plan. Optional per-end-user spend caps (Settings → API limits) stop a single externalUserId from draining the wallet; a capped request fails with 429 USER_SPEND_CAP_REACHED.

8. Debugging

  • Logs tab (Channels → Aiva over API → Logs) shows your recent authenticated API requests with status, error code and duration, plus every callback delivery with its outcome. A failed delivery can be replayed from there with the original payload.
  • Rate limit state is on every response: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. A 429 includes Retry-After in seconds.
  • Errors use one envelope everywhere: { "status": "error", "statusCode": 402, "code": "INSUFFICIENT_CREDITS", "message": "…" }. Branch on code, not on the message text.

The full endpoint and event schemas are in the API reference.

Going live checklist

  1. Key created with only the scopes you need; secrets stored server-side.
  2. Callback endpoint verifies signatures over the raw body and deduplicates on X-Assistify-Delivery-Id, confirmed with Send test.
  3. Idempotency keys are fresh UUIDs per message, persisted with your outbound queue so retries reuse them.
  4. agent.reply and conversation.handoff are handled, so a human takeover reaches your end-user.
  5. Credit balance funded, auto-recharge or a top-up reminder in place, and per-user spend caps set if your end-users are untrusted.