Why Use the Chat API?

The FAQ Ally Chat API allows you to integrate AI agents directly into your custom applications, websites, and mobile apps. Unlike widgets, which provide a pre-built chat interface, the API gives you full control over the user experience, allowing you to create custom interfaces that match your brand and workflow.

The API is secure, easy to use, and includes built-in image support. Responses automatically include relevant images from your training documents when available.

API Overview

🌐 Endpoint

POST https://api.faqally.com/api/agents/{agentId}/chat

Replace {agentId} with your AI agent's ID.

πŸ”‘ Authentication

The Chat API uses API key authentication. Include your API key in the Authorization header:

Authorization: Bearer sk_live_your_api_key_here

You can also use the X-API-Key header:

X-API-Key: sk_live_your_api_key_here

πŸ“ Request Format

Content-Type: application/json

{
  "message": "What is your return policy?",
  "toolsContext": null
}

message (required) β€” the user's question as a non-empty string.

toolsContext (optional) β€” advanced tool-drawer context when your integration uses contextual actions. Omit or pass null for standard Q&A.

βœ… Response Format (v3)

Successful responses use response schema v3. The answer model is resultPresentation β€” the same unified card DTO used by the FAQ Ally app and embedded widget. Top-level flat fields such as response, confidence (0–1), and citations are not returned on the public API wire.

{
  "success": true,
  "responseSchemaVersion": 3,
  "usage": {
    "prompt_tokens": 1200,
    "completion_tokens": 180,
    "total_tokens": 1380
  },
  "resultPresentation": {
    "answerType": "narrative",
    "evidenceType": "retrieval",
    "trust": {
      "status": "verified",
      "confidence": "high",
      "explanation": "Answer verified against company documents."
    },
    "primaryContent": {
      "type": "paragraph",
      "value": "Our return policy allows returns within 30 days of purchase..."
    },
    "responseFormat": "plain",
    "sources": [
      {
        "title": "Return Policy",
        "location": "Section 3 β€” Returns",
        "excerpt": "Items may be returned within 30 days...",
        "contribution": "Return window and eligibility",
        "openPath": "/files/abc123"
      }
    ],
    "explainability": {
      "outcome": "answered",
      "decision": {
        "summary": "I found sufficient supporting information and verified it before generating this answer."
      },
      "searched": {
        "title": "Searched",
        "targets": ["Company Policies", "Customer Support"]
      },
      "evidenceUsed": {
        "title": "Evidence Used",
        "items": ["Return window and eligibility"]
      },
      "confidenceExplanation": "Verified using multiple supporting documents."
    },
    "sections": [
      { "id": "why_this_result", "title": "Why This Result", "content": "..." },
      { "id": "what_was_searched", "title": "Searched", "items": [ ... ] },
      { "id": "sources", "title": "Sources (1)", "items": [ ... ] }
    ],
    "images": [
      {
        "url": "https://api.faqally.com/api/agents/AGENT_ID/images/IMAGE_ID?api_key=...",
        "alt": "Source image"
      }
    ],
    "timestamp": "2026-07-12T18:00:00.000Z",
    "actions": {
      "copy": true,
      "viewDetails": false,
      "openSource": true,
      "download": true
    }
  }
}

Top-level fields:

  • success:true when the chat completed
  • responseSchemaVersion: Always 3 for current integrations
  • usage: Token usage for the request (e.g. prompt_tokens, completion_tokens, total_tokens) when available
  • resultPresentation: The full answer card β€” see below

πŸ“¦ resultPresentation fields

  • primaryContent: Main answer β€” { type: "paragraph" | "scalar" | "comparison", value: string }. Use value for display; append bodyContinued when present.
  • responseFormat:"plain" or "markdown" β€” how to render primaryContent and bodyContinued
  • trust:status (verified, partial_coverage, review_recommended, not_verified, abstained), confidence (high | medium | low), optional explanation one-liner, optional badges
  • title: Optional structured title (e.g. BQE spend ranking)
  • sources: Source cards β€” title, location, excerpt, contribution, openPath for deep links
  • sections: Expandable section payloads keyed by id (mirrors app/widget panels)
  • explainability: Customer-safe β€œwhy this result” breakdown β€” see next section
  • metrics: Optional metric chips (e.g. product guide coverage)
  • structuredProof: BQE / structured computation summary when applicable
  • visualization: Table or chart payload when the user asks for data visualization β€” see Visualizations guide
  • images: Ready-to-use image URLs with authentication in the query string
  • timestamp: ISO timestamp for the answer
  • actions: Hint flags (copy, openSource, etc.) β€” your UI decides what to enable

πŸ” Explainability (resultPresentation.explainability)

Phase 7 explainability gives integrators the same trust breakdown as the FAQ Ally result card, without exposing internal pipeline codes. Use it to build optional detail panels in your UI.

  • outcome:answered, partial, or refused
  • decision.summary: Plain-language β€œwhy this result” text
  • searched.targets: Knowledge areas explored (abstract labels, not raw filenames)
  • evidenceUsed.items: What actually supported the delivered answer
  • answerScope: For partial answers β€” covers / doesNotCover
  • missing: Items that could not be verified
  • computed: BQE β€œhow did you get this number?” rows
  • relatedInformation: Related policies or documents when combination logic applied
  • nextSteps: Only present when there is a concrete recoverable gap (omitted when empty)
  • confidenceExplanation: One-sentence reinforcement aligned with trust

Section IDs in resultPresentation.sections align with explainability: why_this_result, what_was_searched, evidence_used, answer_scope, computed_from, could_not_verify, related_information, next_steps, sources.

🧩 Rendering checklist for integrators

Your application owns the UI. FAQ Ally returns structured facts; you render them. Minimal flow:

// Minimal integrator checklist (v3)
const rp = data.resultPresentation;

// 1. Main answer
display(rp.primaryContent.value);
if (rp.bodyContinued) display(rp.bodyContinued);

// 2. Trust strip
showBadge(rp.trust.status, rp.trust.confidence);
if (rp.trust.explanation) showSubtitle(rp.trust.explanation);

// 3. Explainability (optional detail panels)
const exp = rp.explainability;
if (exp?.decision?.summary) showWhy(exp.decision.summary);
if (exp?.searched?.targets) showSearched(exp.searched.targets);
if (exp?.evidenceUsed?.items) showEvidence(exp.evidenceUsed.items);

// 4. Sources β€” prefer rp.sources; sections[id=sources] mirrors the same data
for (const src of rp.sources || []) {
  renderSource(src.title, src.contribution, src.excerpt, src.openPath);
}

// 5. Images β€” ready-to-use URLs
for (const img of rp.images || []) {
  renderImage(img.url, img.alt);
}

// 6. Charts/tables when user asks for visualization
if (rp.visualization) renderViz(rp.visualization);

Parity note: The FAQ Ally web app and embedded widget render the same resultPresentation shape. API consumers should treat resultPresentation as the single source of truth β€” do not reconstruct answers from deprecated flat fields.

Plan availability

The Chat API is included during the 30-day trial and on Small plans and above. It is not included on Mini or Starter paid plans. Company query limits and API rate limits still apply.

Getting Started

1 Create an API Key

Click Edit on an agent card to open the agent wizard. In Advanced Mode, navigate to the API Setup step and click "Generate New Key".

Important: Copy the full API key immediately: it is only shown once. Store it securely and never expose it in client-side code or commit it to version control.

2 Configure Domain Verification

Domain verification restricts API access to authorized domains. Configure it at the agent level; the same DNS TXT record works for both widgets and API keys. See the Domain Verification guide for setup instructions.

3 Make Your First API Request

Use the API key to send chat messages to your AI agent. Here's a simple cURL example:

curl -X POST https://api.faqally.com/api/agents/YOUR_AGENT_ID/chat \
  -H "Authorization: Bearer sk_live_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"message": "What is your return policy?"}'

Optional Markdown

Agents can be configured to return responses in either Plain Text or Markdown format. Check resultPresentation.responseFormat in the API response.

πŸ“„ Response Format Field

Examples of markdown vs plain responses:

// Markdown response
{
  "success": true,
  "responseSchemaVersion": 3,
  "resultPresentation": {
    "primaryContent": {
      "type": "paragraph",
      "value": "Here's a **formatted** response with *markdown*..."
    },
    "responseFormat": "markdown",
    "trust": { "status": "verified", "confidence": "high" }
  }
}

// Plain text response
{
  "success": true,
  "responseSchemaVersion": 3,
  "resultPresentation": {
    "primaryContent": {
      "type": "paragraph",
      "value": "Here's a plain text response with paragraph breaks."
    },
    "responseFormat": "plain",
    "trust": { "status": "verified", "confidence": "medium" }
  }
}

πŸ”§ Consuming Markdown Responses

If resultPresentation.responseFormat is "markdown", parse primaryContent.value (and bodyContinued if present) as markdown. If it's "plain", display the text directly with preserved line breaks.

JavaScript Example:

const data = await response.json();
const rp = data.resultPresentation;
const text = String(rp?.primaryContent?.value || '');
const continued = String(rp?.bodyContinued || '');
const fullText = continued ? text + '\n\n' + continued : text;

if (rp?.responseFormat === 'markdown') {
  const html = marked.parse(fullText);
  document.getElementById('chat-response').innerHTML = html;
} else {
  const pre = document.createElement('pre');
  pre.style.whiteSpace = 'pre-wrap';
  pre.textContent = fullText;
  document.getElementById('chat-response').appendChild(pre);
}

Python Example:

import markdown  # or mistune, markdown2, etc.

data = response.json()
rp = data.get('resultPresentation') or {}
text = str((rp.get('primaryContent') or {}).get('value') or '')
continued = str(rp.get('bodyContinued') or '')
full_text = f"{text}\n\n{continued}".strip() if continued else text

if rp.get('responseFormat') == 'markdown':
    return markdown.markdown(full_text)
return full_text.replace('\n\n', '<br><br>')

Note: Markdown format includes support for bold, italic, lists, headings, code blocks, and blockquotes. Always sanitize HTML output when rendering markdown to prevent XSS attacks.

Handling Images

When responses include relevant images, the API returns them on resultPresentation.images β€” an array of objects with ready-to-use url and optional alt.

{
  "success": true,
  "responseSchemaVersion": 3,
  "resultPresentation": {
    "primaryContent": {
      "type": "paragraph",
      "value": "Here's how to perform the chemical reaction..."
    },
    "images": [
      {
        "url": "https://api.faqally.com/api/agents/agent123/images/abc123?api_key=sk_live_...",
        "alt": "Source image"
      }
    ]
  }
}

Usage: Use each url directly in <img src="..."> β€” no URL construction is needed.

Note: Image URLs include your API key in the query parameter (required for <img> tags). They may appear in browser history and server logs.

Code Examples

πŸ’» JavaScript (Node.js / Server-Side)

const axios = require('axios');

async function chatWithAgent(message) {
  try {
    const response = await axios.post(
      'https://api.faqally.com/api/agents/YOUR_AGENT_ID/chat',
      { message },
      {
        headers: {
          'Authorization': 'Bearer sk_live_your_api_key_here',
          'Content-Type': 'application/json'
        }
      }
    );

    const data = response.data;
    if (!data.success || data.responseSchemaVersion !== 3 || !data.resultPresentation) {
      throw new Error('Unexpected API response shape');
    }

    const rp = data.resultPresentation;
    const answerText = String(rp.primaryContent?.value || '');
    console.log('Answer:', answerText);
    console.log('Trust:', rp.trust?.status, rp.trust?.confidence);
    console.log('Sources:', rp.sources?.map((s) => s.title));
    if (rp.explainability?.decision?.summary) {
      console.log('Why:', rp.explainability.decision.summary);
    }
    if (rp.images?.length) {
      console.log('Images:', rp.images.map((img) => img.url));
    }
    if (data.usage?.total_tokens != null) {
      console.log('Tokens:', data.usage.total_tokens);
    }
    return data;
  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
    throw error;
  }
}

// Usage
chatWithAgent('What is your return policy?');

🐍 Python

import requests

def chat_with_agent(message):
    url = 'https://api.faqally.com/api/agents/YOUR_AGENT_ID/chat'
    headers = {
        'Authorization': 'Bearer sk_live_your_api_key_here',
        'Content-Type': 'application/json'
    }
    data = {'message': message}

    response = requests.post(url, json=data, headers=headers)
    response.raise_for_status()

    result = response.json()
    if not result.get('success') or result.get('responseSchemaVersion') != 3:
        raise ValueError('Unexpected API response shape')

    rp = result.get('resultPresentation') or {}
    primary = rp.get('primaryContent') or {}
    answer_text = str(primary.get('value') or '')
    print(f"Answer: {answer_text}")
    trust = rp.get('trust') or {}
    print(f"Trust: {trust.get('status')} ({trust.get('confidence')})")
    sources = rp.get('sources') or []
    print(f"Sources: {[s.get('title') for s in sources]}")
    explain = (rp.get('explainability') or {}).get('decision') or {}
    if explain.get('summary'):
        print(f"Why: {explain['summary']}")
    images = rp.get('images') or []
    if images:
        print(f"Images: {[img.get('url') for img in images]}")
    usage = result.get('usage') or {}
    if usage.get('total_tokens') is not None:
        print(f"Tokens: {usage['total_tokens']}")
    return result

# Usage
chat_with_agent('What is your return policy?')

πŸ”΅ C# / .NET

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class FaqAllyClient
{
    private readonly HttpClient _httpClient;
    private readonly string _agentId;

    public FaqAllyClient(string apiKey, string agentId)
    {
        _httpClient = new HttpClient();
        _agentId = agentId;
        _httpClient.BaseAddress = new Uri("https://api.faqally.com");
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
    }

    public async Task<JsonDocument> ChatAsync(string message)
    {
        var request = new { message };
        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync(
            $"/api/agents/{_agentId}/chat",
            content
        );

        response.EnsureSuccessStatusCode();
        var responseJson = await response.Content.ReadAsStringAsync();
        return JsonDocument.Parse(responseJson);
    }
}

// Parse resultPresentation.primaryContent.value, trust, sources, explainability from JsonDocument

Security Best Practices

  • Never expose API keys in client-side code: Always call the API from your backend server
  • Use environment variables: Store API keys securely, never in code
  • Enable domain verification: Restricts API access to authorized domains even if a key is compromised
  • Rotate keys regularly: Regenerate API keys periodically

Recommended Pattern: Frontend β†’ Your Backend API β†’ FAQ Ally API. This keeps API keys secure on your server.

Rate Limiting

API keys have built-in rate limiting to protect your resources:

  • Default: 60 requests per minute per API key
  • Company limits: Respects your plan's query limits
  • Rate limit exceeded: Returns 429 Too Many Requests

If you need higher rate limits, contact support to discuss your requirements.

Error Handling

πŸ“‹ Common Error Responses

  • 401 Unauthorized: Invalid or inactive API key
  • 403 Forbidden: Domain not authorized or plan limit exceeded
  • 400 Bad Request: Invalid request format or missing message
  • 404 Not Found: Agent not found
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error (try again later)

πŸ” Error Response Format

{
  "success": false,
  "error": "Error Type",
  "message": "Human-readable error message"
}

Next Steps

Now that you understand the Chat API, you're ready to integrate FAQ Ally into your applications: