Get to your first reply in five minutes.
This guide walks you through creating a bot, training it on your knowledge, picking a model, and dropping the widget on your site.
1. Quickstart
Create an account at signup, name your first bot, then drop one URL or upload a PDF on the Sources tab. You can chat with the bot in the Playground immediately.
2. Adding sources
The Sources panel accepts websites (one URL or a sitemap), PDFs, .docx, .txt, .csv, Notion pages, and Google Drive folders. Each source can be re-indexed on demand or on a schedule. You can prune individual chunks if you want the bot to forget something.
3. Choosing a model
Open the Model tab and pick from Anthropic Claude, OpenAI GPT, Google Gemini, Groq-hosted Llama, or Mistral. You can override the model per bot and define a fallback chain for the unlikely case a provider has an outage.
4. Embedding the widget
Paste this snippet right before </body> on any page:
<script src="https://chatapp.ai-sns.io/embed.min.js" data-agent-id="YOUR_AGENT_ID" defer></script>
For server-side rendering or React, call the REST API directly — see the code examples below.
5. REST API
Everything in the dashboard is available over a JSON REST API. Authenticate with a Bearer token from /api/auth/login, then call any endpoint. See the full API Reference →
# 1) get a token
curl -s -X POST https://aisnsio-aisnschatwebsite.hf.space/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"********"}'
# → { "user": {…}, "token": "eyJhbGciOi…" }
# 2) call any endpoint with it
TOKEN="eyJhbGciOi…"
curl -s https://aisnsio-aisnschatwebsite.hf.space/api/chat \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"agentId":1,"message":"How do refunds work?"}'6. Webhooks
Subscribe an HTTPS endpoint and we will POST to it when something happens in your workspace.
Events
| Event | Fires when |
|---|---|
| message.created | a visitor sends a message and your agent answers |
| handoff.requested | the agent asks for a human — carries reason and confidence |
| lead.captured | a lead is captured, from the chat or the widget’s lead form |
reason on handoff.requested is one of no_grounding, low_confidence, user_asked, provider_unavailable, abuse_flagged.
Deprecated: leads.submit was the earlier name for lead.captured. Endpoints already subscribed to it keep receiving these events for one more release, then it is removed. New integrations should use lead.captured.
The request we send
POST https://your-endpoint.example.com/hooks
Content-Type: application/json
User-Agent: AI-SNS-CHAT-Webhooks/1
X-AISNS-Event: lead.captured
X-AISNS-Delivery: 6f1f4e2c-0b6e-4a53-9a1e-2f0b0f5f9d21
X-AISNS-Timestamp: 1700000000
X-AISNS-Signature: t=1700000000,v1=6a1c…
{"id":"6f1f4e2c-…","event":"lead.captured","data":{ … },"sent_at":"2026-08-16T04:00:00.000Z"}X-AISNS-Delivery is stable across retries of the same delivery — use it to make your handler idempotent.
Verifying the signature
Every request is signed with HMAC-SHA256 using your workspace’s signing secret. Find it in the dashboard under Webhooks → Signing secret; it looks like whsec_….
X-AISNS-Signature has the form t=<unix-seconds>,v1=<hex>. The signed string is the timestamp, a full stop, and the raw request body:
signed_payload = "{t}.{raw_body}"
v1 = hex( HMAC-SHA256( signing_secret, signed_payload ) )Sign the raw bytes you received. If your framework parses JSON and you re-serialise it, key order or whitespace can change and the signature will not match — this is the single most common mistake.
Reject anything older than 5 minutes (compare t to your own clock). That is what stops a captured request being replayed at you later.
Node.js
import crypto from 'node:crypto';
export function verifyAisnsSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(
String(header || '').split(',').map((p) => p.split('=')).filter((p) => p.length === 2),
);
const t = Number(parts.t);
if (!Number.isFinite(t) || !parts.v1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSeconds) return false; // replayed or stale
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected), b = Buffer.from(parts.v1);
return a.length === b.length && crypto.timingSafeEqual(a, b); // constant time
}Python
import hashlib, hmac, time
def verify_aisns_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
try:
t = int(parts["t"]); v1 = parts["v1"]
except (KeyError, ValueError):
return False
if abs(int(time.time()) - t) > tolerance: # replayed or stale
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1) # constant timeRetries
If your endpoint does not answer with a 2xx we retry twice more — after 2 seconds, then after 10. We retry on a network error, 429, or any 5xx. We do not retry a 4xx: that is your endpoint telling us the request itself is wrong, and sending it again cannot help.
Every attempt carries the same X-AISNS-Delivery, so a handler that has already processed a delivery id can safely ignore the repeat.
Each delivery, successful or not, appears in Webhooks → Deliveries in the dashboard with its status, attempt count and error.
Requirements
Your endpoint must be a public HTTPS URL. http://, localhost, private ranges and link-local addresses are rejected when you save the webhook — we will not make requests into a private network on someone else’s behalf.
7. Calling the API from your code
There are no official client packages yet — nothing on npm, PyPI, Go modules or RubyGems. The REST API in section 5 is the interface: plain JSON over HTTPS with a Bearer token, so any language calls it with the HTTP client it already has. If we publish packages we will list them here.
// JavaScript / TypeScript — no dependencies, just fetch
const res = await fetch("https://aisnsio-aisnschatwebsite.hf.space/api/chat", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.AISNS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: "What's your return policy?" }),
});
const { reply } = await res.json();
console.log(reply);# Python — requests
import os, requests
r = requests.post(
"https://aisnsio-aisnschatwebsite.hf.space/api/chat",
headers={"Authorization": f"Bearer {os.environ['AISNS_TOKEN']}"},
json={"message": "What's your return policy?"},
)
print(r.json()["reply"])Every endpoint follows the same shape — see the API Reference for the full list.
Need help?
Drop us a line on the contact page — a real human will read it.


