Aiva over API reference

Endpoints, callback events, headers, and error codes for the Aiva over API REST surface.

Reference for the REST surface behind Aiva over API. All endpoints are relative to:

https://assistify.chat/api/v1

Responses use one envelope:

// success
{ "status": "success", "data": { /* … */ } }
// error
{ "status": "error", "statusCode": 402, "code": "INSUFFICIENT_CREDITS", "message": "…" }

Authentication

Every request carries an API key as a Bearer token:

Authorization: Bearer ak_<keyId>_<secret>

Keys are created in the dashboard under Channels → Aiva over API → Keys. The full key is shown once at creation and only a hash is stored; revoking a key stops it on the next request.

Scopes

ScopeGrants
messages:writePOST /v1/messages
identity:verifyPOST /v1/identity/verify
conversations:readGET /v1/conversations/:id

A call with a key missing the required scope fails with 403 FORBIDDEN.

Rate limits

120 requests per 60 seconds per key (default). Every response carries the current state in both header families:

HeaderMeaning
X-RateLimit-LimitRequests allowed per window
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetWhen the window resets (unix seconds)
RateLimit-*The same values in the IETF draft format

Exceeding the limit returns 429 with Retry-After (seconds). Unauthenticated traffic is limited per IP before it reaches any per-key bucket.

Idempotency

POST /v1/messages requires an idempotencyKey (UUID):

  • A retry with the same key returns the same messageId with status: "duplicate", is never re-dispatched, and is never charged again. Concurrent duplicates collapse to one message.
  • Use a fresh UUID per logical message; reuse it only when retrying that exact message.

Endpoints

POST /v1/messages

Submit an end-user message. Scope: messages:write. Aiva processes it asynchronously; the reply arrives via callback or polling.

Body

FieldTypeRequiredNotes
externalConversationIdstring (1–200)yesYour stable conversation id. One conversation per id.
contentstring (1–5000)yesThe end-user's message text.
idempotencyKeystring (UUID)yesRetry-safe anchor.
externalUserIdstring (1–200)noYour stable end-user id. Enables identity and per-user spend caps.

channel is not accepted; it is forced to API server-side.

Response 202

{ "status": "success", "data": {
  "conversationId": "…",
  "messageId": "…",
  "status": "queued"    // "queued" | "duplicate" | "skipped" (+ "reason" when skipped)
}}

Errors: 400 validation, 401 auth, 402 INSUFFICIENT_CREDITS, 403 scope/access, 429 USER_SPEND_CAP_REACHED, 429 rate limit.

POST /v1/identity/verify

Initiate an identity verification ceremony. Scope: identity:verify. The API can only initiate; verification is completed by the end-user out-of-band and can never be asserted by a key.

Body

FieldTypeRequiredNotes
externalUserIdstring (1–200)yesThe end-user to verify.
method"magic_link" | "oauth"yesoauth is not available here and returns status: "unsupported_here".
emailstring (email)for magic_linkWhere the single-use link is sent.

Response 202

{ "status": "success", "data": { "method": "magic_link", "status": "sent" } }

When the user completes the ceremony, an identity.verified event is pushed to every key that owns an API conversation for that user.

GET /v1/conversations/:externalConversationId

Read conversation state and message history. Scope: conversations:read. Use it to poll for replies when no callback is configured.

Response 200

{ "status": "success", "data": {
  "externalConversationId": "order-4821",
  "conversationId": "…",
  "status": "OPEN",
  "createdAt": "…",
  "lastMessageAt": "…",
  "messages": [
    { "id": "…", "seq": 1, "senderType": "VISITOR", "content": "…", "createdAt": "…" },
    { "id": "…", "seq": 2, "senderType": "AI",      "content": "…", "createdAt": "…" }
  ]
}}

Text messages only, in seq order; internal notes and system events are excluded. Unknown id returns 404 NOT_FOUND.

Callback events

Configured per key (an https callbackUrl set at creation). Each delivery is an HTTP POST with these headers:

HeaderMeaning
X-Assistify-EventEvent name
X-Assistify-Signaturesha256=<hex> HMAC of `${timestamp}.${rawBody}` with the key's callback secret
X-Assistify-TimestampUnix seconds, signed into the HMAC
X-Assistify-Delivery-IdStable per-delivery UUID, reused across retries. Deduplicate on it.

Events

EventWhenBody fields
ai.replyAiva generated a replyevent, conversationId, messageId, seq, content, visibility, poweredBy, timestamp
agent.replyA human agent replied from the dashboardevent, conversationId, messageId, seq, content, agentName, timestamp
conversation.handoffThe conversation escalated to a humanevent, conversationId, reason, summary, nextStep, isUrgent, timestamp
identity.verifiedAn end-user completed verificationevent, externalUserId, tier: "verified", timestamp
test.pingYou pressed Send test on a keyevent, message, keyId, timestamp

conversationId in callback bodies is always your externalConversationId. visibility on ai.reply is "public" or "private" (private may reference personal data of a verified user; render it only behind that user's authentication).

Delivery semantics

  • Respond 2xx within 5 seconds; anything else counts as a failure.
  • Failures are retried with exponential backoff (5 attempts). Delivery is degraded, never disabled: a broken endpoint does not switch off the channel, and exhausted deliveries stay visible in the dashboard Logs tab, where they can be replayed.
  • A replay from the Logs tab re-sends the original payload with a new delivery id.
  • Callback URLs must be https and publicly resolvable. URLs resolving to private or loopback address space are blocked at delivery time.

Identity model

TierMeaning
anonymousNo externalUserId supplied; each message creates or reuses an anonymous contact.
unverifiedexternalUserId supplied; asserted by the key, does not unlock personal tools.
verifiedThe end-user completed a verification ceremony; personal and account-scoped tools unlock.

Personal-data tools are gated server-side on verified identity; a key cannot forge it.

Billing

Usage is billed per token from a prepaid credit wallet (rate and balance under Channels → Aiva over API → Credits). Each request is gated before generation (insufficient balance fails closed with 402, nothing is charged) and settled once after generation. Idempotent retries and redeliveries never double-charge. Optional rolling per-end-user spend caps return 429 USER_SPEND_CAP_REACHED when reached.

Errors

HTTPcodeCause
400VALIDATION_ERRORBad or missing body field (for example a non-UUID idempotencyKey).
401UNAUTHORIZEDMissing, malformed, unknown, or revoked key.
402INSUFFICIENT_CREDITSWallet cannot cover a worst-case generation. Top up.
403FORBIDDENKey lacks the required scope.
403API_NOT_ENABLEDThe public API is not available right now.
403API_ACCESS_REQUIREDYour workspace has no active API access. Request it in the dashboard.
403API_ACCESS_REVOKEDYour workspace's API access was revoked.
404NOT_FOUNDUnknown externalConversationId.
429USER_SPEND_CAP_REACHEDThe end-user's rolling spend cap is reached.
429(rate limit)Per-key or per-IP rate limit. Honor Retry-After.