Docs & API reference
Everything you need to connect your AI chatbot (on any AI model, including Grok, ChatGPT, Claude and Gemini), AI agents, n8n/Make automations, Google Sheets and CRM to ProofMyAI. Most setups take under 10 minutes and many need no code at all. Prefer video? Watch the 3-minute setup tutorial β or see alerts arrive in the live demo β.
π Quick start
- Create a free account (50 conversations a month free).
- Open your project β Settings and copy your API key (it starts with
ap_live_). Each project has its own key. - Pick what you want to monitor: upload chat transcripts, connect n8n/Make, or send events to the API below.
- Add a WhatsApp, Slack, Teams, email, Telegram or webhook channel under Settings β Notifications so you hear about problems.
π§© Connect without code
- Chat transcripts: export conversations from Intercom, Tidio, Crisp, Zendesk or any bot as CSV/JSON and upload them in Chatbot audits. Add your help articles first so answers are checked against them.
- Nightly bot tests: in Chatbot tests, paste your bot's HTTP endpoint and a list of questions with the facts a right answer must include. ProofMyAI asks your bot every night.
- n8n: n8n β Settings β n8n API β create a key. In ProofMyAI open n8n & Make β Connect n8n and paste your n8n URL and key. Executions are pulled every 15 minutes, and workflows that stop running or go quiet are caught too. For instant alerts (seconds, not minutes), also add an Error Trigger workflow in n8n that posts to POST /workflow-runs.
- Make: Make β Profile β API access β add a token with
scenarios:read. Connect Make in ProofMyAI with your scenario IDs. - WhatsApp live monitoring (WAHA): add the webhook
https://proofmyai.com/api/v1/whatsapp/wahato your WAHA session with the eventmessage.anyand headerX-Api-Keyset to your project key. Customer messages and bot replies are paired and checked; phone numbers are replaced with anonymous codes. - Intercom (Fin): in Connect β Intercom, paste an Intercom access token. Every Fin or bot reply is checked automatically.
- Google Sheets: in Google Sheets, paste a sheet link. Alerts when it stops updating, shows #REF! or other errors, a key column comes in empty, columns change or rows are duplicated.
- CRM: in CRM, paste a read-only token for HubSpot, Salesforce, Pipedrive, Zoho CRM, GoHighLevel and others (see CRM monitoring).
- Watch for anything, in your own words: every feature page has a "Watch forβ¦" box. Write what to watch and when to be told (e.g. "Alert me if the Order sync workflow runs fewer than 10 times a day"). ProofMyAI shows how it understood the request and how often it would have alerted in the last 7 days, then checks it on every new event and every 15 minutes.
- Grok, ChatGPT, Claude or Gemini bots: any model works. Connect β Grok (xAI) chatbot has a ready-made example; the same call works for every model (see POST /chat-events).
- Twilio / WhatsApp: in Live tracking, connect Twilio with a read-only API key to check bot replies on SMS and WhatsApp.
π Authentication
Send your project API key with every request, in either header:
Authorization: Bearer ap_live_xxxxxxxxxxxxxxxx
# or
X-API-Key: ap_live_xxxxxxxxxxxxxxxxBase URL: https://proofmyai.com/api/v1. Bodies are JSON (Content-Type: application/json), up to 2 MB. Keep the key on your server; don't put it in browser code. You can regenerate it in Settings at any time.
π¬ POST /chat-events: live chatbot monitoring
Send each conversation as it happens, either the full message history or just the newest question and answer. Personal data is masked before anything is stored or checked. Replies are graded in the background against your help articles and rules.
| Field | Type | Description |
|---|---|---|
conversation_id | string, required | Your ID for the conversation. Sending the same ID again adds only the new turns. |
messages | array | Full history: [{ role: "user" | "assistant", content }]. |
question | string | The customer's message. Send it alone when the customer writes, and ProofMyAI alerts you if the bot never replies. |
answer | string | The bot's reply to question. |
bot_name | string | Optional label when you run several bots. |
latency_ms | integer | Optional: how long the reply took. |
cost_usd | number | Optional: what the reply cost. |
error | string | Optional: an error your bot hit while replying. |
retrieved | string[] | Optional (RAG bots): the text chunks your bot retrieved for this reply. When an answer is wrong, ProofMyAI says whether it was a retrieval problem (the right fact wasn't fetched), a generation problem (it was fetched and ignored) or missing documentation. Used for the check only, never stored. |
trace_id | string | Optional: your id for one end-to-end request (e.g. an order or ticket number). Send the same id to /check, /agent-runs, /workflow-runs, /approvals and /verify, then open Live tracking β π Traces to see every step on one timeline, with the first failing step highlighted. Kept up to 90 days (or your retention setting). |
Send messages, or question + answer, or just question.
curl -X POST https://proofmyai.com/api/v1/chat-events \
-H "Authorization: Bearer $PROOFMYAI_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversation_id": "chat-8841",
"question": "How much is shipping to Germany?",
"answer": "Shipping to Germany is free on all orders.",
"latency_ms": 1850
}'Response 202: {"accepted": 1}. Add ?wait=1 to wait for the verdicts (slower; useful for testing):
{"graded": 1, "results": [{"turn_index": 0, "verdict": "hallucination", "label": "Made up",
"severity": "high", "reason": "The shipping article says Germany costs β¬9.90; free shipping is not mentioned."}]}Verdicts: correct, unsupported (not in your docs), hallucination (made up), should_escalate, off_policy, unclear.
Tip: call the API without waiting for it (fire-and-forget) so your bot never slows down. The Live tracking page in your dashboard has ready-made cURL, JavaScript and Python snippets that do this safely, plus no-code steps for Intercom, Zendesk, Crisp and Tidio through n8n or Make.
π POST /check: stop a bad answer before it's sent
The pre-send check (guardrail). Before your chatbot sends a reply, or before an AI agent acts on its decision (a refund, a discount, a booking), send the question and the draft reply. ProofMyAI checks it against your help docs and rules and answers in a few seconds. The answer is also monitored as usual.
curl -X POST https://proofmyai.com/api/v1/check -H "Authorization: Bearer ap_live_your_key" -H "Content-Type: application/json" -d '{"question": "Can I return a sale item for a refund?",
"answer": "Yes, every item gets a full refund within 30 days."}'{"decision": "block", "verdict": "hallucination", "label": "Made up", "severity": "high",
"reason": "The returns article says sale items get store credit only.",
"stated_as_fact": true,
"source": "Returns & refunds",
"evidence": "Sale items can be returned for store credit only, not a refund.",
"area": "Returns & refunds", "area_autonomy": "human_review",
"advice": "Do not send. Hand the customer to a person."}evidence is the exact sentence from your help docs behind the verdict. A quote from the AI judge is only kept if it appears in your docs word for word, so the evidence is never made up. It's also shown on every checked answer in your dashboard and in the CSV export.
stated_as_fact is true when a wrong answer was said with full confidence ("Yes, all itemsβ¦", a specific price or deadline, no hedge). Customers act on those without double-checking, so they are rated a level more serious. Such answers are marked "Stated as fact" in your dashboard.
| Field | Type | Description |
|---|---|---|
allow | decision | Safe to send or act on. |
review | decision | Hold it for a person to approve, or send a safe fallback such as "Let me check that for you". Every answer in an area your Trust report marks π΄ returns review. |
block | decision | Don't send it: it's made up, against your policy, or should go to a human. |
/chat-events?wait=1 also returns a decision for each reply. Each check counts as one conversation on your plan. If the check fails or times out, fall back to sending the reply as usual (or to a human for high-stakes actions).
π§ͺ POST /test-runs: regression tests in your deploy pipeline
Changed a prompt, a model or your help docs? Run your saved Chatbot tests before the change goes live. ProofMyAI asks your bot every test question right now and answers with pass/fail per question. Point url at a preview or staging deployment to test a pull request. CI runs don't raise incidents and don't replace the nightly run.
| Field | Type | Description |
|---|---|---|
bot | string or number | Name or id of the saved bot. Optional when the project has one bot. |
url | string | Optional: test this address instead of the saved one (same body template and answer location). |
min_pass_rate | number 0β1 | Share of questions that must pass for ok to be true. Default 1 (all). |
curl -X POST https://proofmyai.com/api/v1/test-runs \
-H "Authorization: Bearer $PROOFMYAI_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://preview-42.yourbot.com/chat"}'
{"ok": false, "bot": "Support bot", "passed": 19, "failed": 1, "total": 20, "pass_rate": 0.95,
"failures": [{"question": "Can I return a sale item?", "answer": "Yes, full refund.",
"reason": "The answer contains a forbidden statement: \"full refund\"."}]}GitHub Actions example: fail the build when answers get worse.
# .github/workflows/bot-tests.yml
name: Chatbot regression tests
on: [pull_request]
jobs:
proofmyai:
runs-on: ubuntu-latest
steps:
- name: Run ProofMyAI tests
run: |
RESULT=$(curl -s -X POST https://proofmyai.com/api/v1/test-runs \
-H "Authorization: Bearer ${{ secrets.PROOFMYAI_KEY }}" -H "Content-Type: application/json" \
-d '{"url": "${{ vars.PREVIEW_BOT_URL }}"}')
echo "$RESULT"
echo "$RESULT" | jq -e '.ok == true' > /dev/nullLimit: 30 test runs per project per hour. Each question is graded by the AI judge when AI checking is on, otherwise by the expected facts and forbidden phrases you saved.
β POST /approvals: a person approves what matters
"Automate the draft, never the send." Before your agent does something important (issues a refund, emails a customer, pays an invoice, writes to your CRM), it submits the action. Routine, reversible actions come back approved straight away. These wait as pending until someone clicks Approve or Reject on the Approvals page (you get an alert):
- anything marked
"reversible": false; - the first action of a new
category(for example a new supplier). Click "Approve & always allow" and it becomes a rule; - an
amountat or over your limit; - text that promises a refund, discount, deadline or compensation.
curl -X POST https://proofmyai.com/api/v1/approvals -H "Authorization: Bearer ap_live_your_key" -H "Content-Type: application/json" -d '{"action": "refund", "summary": "Refund order #4471 for Sara K.", "amount": 89, "currency": "GBP",
"category": "refunds", "reversible": false, "agent_name": "Support agent", "action_id": "refund-4471",
"details": "Hi Sara, I have refunded Β£89 to your card. It will arrive within 5 days.",
"callback_url": "https://your-n8n.example.com/webhook/approval"}'{"id": 812, "action_id": "refund-4471", "status": "pending", "needs_human": true,
"reason": "This can't be undone, so a person approves it first.", "commitments": ["a refund", "a deadline"],
"poll_url": "https://proofmyai.com/api/v1/approvals/812"}Then wait for the decision: ProofMyAI POSTs {"id", "action_id", "status", "decided_by", "note"} to your callback_url, or poll GET /approvals/812. Only act when status is approved. Requests expire after 24 hours (expires_in_hours). If the agent submits an action again after a person rejected it, it stays rejected and you get an alert. Every decision is logged with who decided, when and their note. In n8n: HTTP Request node β Wait node (resume on webhook) β IF status = approved.
Safety guarantees (each one is covered by an automated test that runs before every release):
- The same action sent several times at once, or again while it waits, is one request, never a duplicate refund or payment.
- A rejected action stays rejected on every retry (matched by
action_idor identical content), raises one "rejected action retried" alert, and can't be flipped to approved. - Two people deciding at the same moment: only the first decision counts.
- Expired requests can never be approved later; resubmitting asks a person again.
- If your
callback_urlis down, the decision is still recorded and your agent can pollpoll_urlfor it. - Every decision records who decided, when and why.
π Verify results: don't take the agent's word for it
Agents and workflows can report "done" when the CRM record never saved or the email never went out. Tell ProofMyAI where the result should be, and it checks the real system. If the result isn't there, it checks again 30 seconds later, then raises a high alert.
On an agent run, add verify to your /agent-runs body (checked about 10 seconds after the run):
"verify": {"url": "https://api.yourcrm.com/v1/leads/8812", "headers": {"Authorization": "Bearer crm_key"},
"expect_text": "sara@shop.co", "delay_seconds": 10}From any workflow (for example an n8n HTTP Request node after "Create record"):
curl -X POST https://proofmyai.com/api/v1/verify -H "Authorization: Bearer ap_live_your_key" -H "Content-Type: application/json" -d '{"name": "Lead sync", "reference": "lead 8812", "url": "https://api.yourcrm.com/v1/leads/8812",
"headers": {"Authorization": "Bearer crm_key"}, "expect_text": "sara@shop.co"}'Response: {"passed": false, "detail": "api.yourcrm.com answered 404. The record may not exist."}. Passes when the URL answers 2xx (or expect_status) and, if set, contains expect_text. Headers are used for the check only and are never stored. Only public URLs can be checked.
π Heartbeats: catch the job that never ran
Error handlers only fire when a job runs and fails. A cron that stopped, a dead server or a disabled trigger fails in silence. Add a heartbeat on the n8n & Make page, then call its URL at the end of the job:
curl -fsS https://proofmyai.com/api/hb/hb_your_token # finished OK
curl -fsS "https://proofmyai.com/api/hb/hb_your_token/fail?msg=disk%20full" # report a failure nowIf the check-in doesn't arrive within the schedule plus a grace period (20%, at least 5 minutes), you get an alert. It clears on the next check-in. No API key is needed: the URL is the secret, so keep it private.
ποΈ CRM monitoring
Connect your CRM on the CRM page. It's read-only, and ProofMyAI stores only hashes and filled/empty flags, never names, emails or phone numbers. Direct connectors: HubSpot, Salesforce, Pipedrive, Zoho CRM, GoHighLevel, Freshsales, Close, ActiveCampaign and Microsoft Dynamics 365, checked every 15 minutes. You get alerts when new leads stop arriving (learned from your CRM's rhythm), new records miss an email/phone or owner, duplicates appear, hundreds of records change at once, leads aren't contacted in time, or the connection fails.
For any other CRM (Monday, Attio, Copper, Keap, Bitrix24, Odoo, Airtableβ¦), send records from n8n, Make or Zapier when they are created or updated:
curl -X POST https://proofmyai.com/api/v1/crm-records -H "Authorization: Bearer ap_live_your_key" -H "Content-Type: application/json" -d '{"source": "Monday CRM", "records": [{"id": "123", "kind": "lead", "created_at": "2026-10-18T09:30:00Z",
"updated_at": "2026-10-18T09:30:00Z", "email": "sara@shop.co", "phone": "+44 7700 900123",
"name": "Sara K.", "owner": "Ali", "last_activity_at": null}]}'Response 202: {"received": 1, "new": 1, "duplicates": 0}. Up to 500 records per call; kind is contact, lead or deal.
π POST /feedback: customer ratings
Send each thumbs up/down or star rating your customers give the bot. ProofMyAI shows customer satisfaction overall and per topic on the Analytics page. Use the same conversation_id you send to /chat-events.
curl -X POST https://proofmyai.com/api/v1/feedback -H "Authorization: Bearer ap_live_your_key" -H "Content-Type: application/json" -d '{"conversation_id": "chat-8812", "rating": "down", "comment": "Wrong delivery price"}'rating: "up" / "down", true / false, whole stars 1β5, or a decimal score from 0 to 1. Optional: turn_index (which reply was rated) and comment. Response 201: {"ok": true, "id": 42}.
π€ POST /agent-runs: AI agent monitoring
Send one request when an agent run finishes. ProofMyAI checks it for loops, tool errors, runaway cost, slow runs, empty output and answers the tools didn't support.
| Field | Type | Description |
|---|---|---|
run_id | string, required | Unique ID of this run. Sending the same run_id twice returns 409. |
agent_name | string, required | Which agent ran, e.g. "refund-agent". |
goal | string | What the agent was asked to do. |
status | string | success (default), error, timeout or cancelled. Send running when the run starts (just run_id and agent_name) to be alerted if it never finishes. |
final_output | string | The agent's final answer or result. |
steps | array | Each step: { type: llm | tool | retrieval | other, name, input, output, error, duration_ms, tokens, cost_usd }. |
curl -X POST https://proofmyai.com/api/v1/agent-runs \
-H "Authorization: Bearer $PROOFMYAI_KEY" \
-H "Content-Type: application/json" \
-d '{
"run_id": "run_2026_0412",
"agent_name": "refund-agent",
"goal": "Refund order #1043",
"status": "success",
"final_output": "Refund of $49 issued.",
"steps": [
{"type": "tool", "name": "lookup_order", "output": {"total": 49}, "duration_ms": 320},
{"type": "tool", "name": "issue_refund", "error": "Payment API timeout", "duration_ms": 30000, "cost_usd": 0.002}
]
}'Response 201 (example):
{"id": 812, "score": 55, "issues": [
{"code": "TOOL_ERRORS", "severity": "high", "message": "..."},
{"code": "ENDED_ON_ERROR", "severity": "medium", "message": "The last step failed but the run still reported success."}]}Issue codes: RUN_FAILED, TOOL_ERRORS, LOOP_DETECTED, TOO_MANY_STEPS, OVER_BUDGET, SLOW_RUN, EMPTY_OUTPUT, ENDED_ON_ERROR, DESTRUCTIVE_ACTION, plus from the AI review GOAL_NOT_MET and UNGROUNDED_OUTPUT. Set your step, cost and time limits in project Settings.
Destructive actions. Tool calls that delete, drop, wipe or overwrite data (a tool named delete_record or volumeDelete, DROP TABLE, DELETE FROM without WHERE, rm -rf, git push --force, an HTTP DELETE) are flagged DESTRUCTIVE_ACTION: high, with an alert, when they touch production, backups or everything at once. Send these actions to /approvals with reversible: false first. If a person approved one with the same trace_id, it is recorded but not alerted.
Agents that go quiet. ProofMyAI learns each agent's normal rhythm and alerts (AGENT_STOPPED) when it stops running, with no settings. To catch runs that start but never finish (hung, crashed, killed), send {"run_id": "...", "agent_name": "...", "status": "running"} when a run starts (answers 202), then the finished run with the same run_id. Starts with no result are alerted as AGENT_RUN_UNFINISHED.
π‘ OpenTelemetry traces (no code changes)
Already instrumented with OpenTelemetry, or using a framework that is (LangChain/LangGraph, CrewAI, OpenAI Agents SDK, LlamaIndex, Vercel AI SDK, or OpenLLMetry/OpenInference instrumentation)? Point the exporter at ProofMyAI with three environment variables. Each finished trace becomes an agent run with the same checks: failed steps, loops, runaway cost and latency, empty output, and step-by-step timing.
OTEL_EXPORTER_OTLP_ENDPOINT=https://proofmyai.com/api/v1/otel
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer ap_live_your_key
OTEL_EXPORTER_OTLP_PROTOCOL=http/jsonSpans that follow the OpenTelemetry GenAI conventions (gen_ai.*) or OpenInference (openinference.span.kind, input.value, output.value) become LLM, tool and retrieval steps, with tokens, timing and errors. The agent name comes from gen_ai.agent.name or your service.name. Send OTLP as JSON; protobuf isn't supported yet.
π POST /calls: voice bots and phone AI
Send each finished phone call. Every bot answer is checked like a chat reply, and the call is checked for the failure staff testing misses: the caller asked for a person and was never transferred, or the bot said it was transferring them and the transfer never happened.
- Vapi: set your assistant's Server URL to
https://proofmyai.com/api/v1/callswith the headerAuthorization: Bearer ap_live_.... Only theend-of-call-reportmessage is used; others are ignored. - Retell: set the agent's webhook URL to
https://proofmyai.com/api/v1/calls?key=ap_live_...(Retell can't add headers). Thecall_endedevent is used. - Any other provider: POST the call as below.
| Field | Type | Description |
|---|---|---|
call_id | string, required | Your id for the call. Sending the same call twice is ignored. |
transcript | array or string | [{ role: user | assistant, content }], or text like "AI: Hello\nUser: Hi". |
transferred | boolean | Whether the call was transferred to a person. If you can't tell, send ended_reason instead. |
ended_reason | string | How the call ended, e.g. customer-hung-up or call_transfer. |
bot_name | string | Optional: which voice bot. |
duration_seconds | number | Optional. |
curl -X POST https://proofmyai.com/api/v1/calls \
-H "Authorization: Bearer $PROOFMYAI_KEY" \
-H "Content-Type: application/json" \
-d '{"call_id": "c_1029", "bot_name": "Front desk", "transferred": false,
"transcript": [{"role": "assistant", "content": "Village offices, how can I help?"},
{"role": "user", "content": "Can I speak to someone in Parks and Recreation?"},
{"role": "assistant", "content": "Sure, transferring you now."}]}'Response 201: {"call_id": "c_1029", "answers_checked": 2, "issues": [{"code": "CALL_NOT_TRANSFERRED", "severity": "high", ...}]}. Codes: CALL_NOT_TRANSFERRED (high, alerted) and TRANSFER_NOT_OFFERED (medium, when you don't send whether it transferred and the bot never offered to).
π€ Promised follow-ups: "I'll get back to you"
A reply like "I'll check with the warehouse and get back to you" breaks no policy, so answer checks pass it. But it's a promise, and if no task exists behind it the customer just waits. ProofMyAI tracks every promise in live chats until it's kept:
- A task exists: send
task_idwith the bot's reply on /chat-events, or callPOST /followupswhen the task is created. - The bot followed up: a later bot reply in the same conversation that isn't another promise.
Otherwise you get PROMISE_NOT_KEPT once the deadline passes (24 hours by default, set in Settings), and PROMISE_CHASED straight away if the customer writes "any update?" first. On /check, a reply that promises a follow-up without a task_id comes back as review, with promise and promise_has_task: false.
curl -X POST https://proofmyai.com/api/v1/followups \
-H "Authorization: Bearer $PROOFMYAI_KEY" \
-H "Content-Type: application/json" \
-d '{"conversation_id": "chat_8812", "bot_name": "Website bot", "task_id": "odoo-task-4471"}'Use the same conversation_id and bot_name you send to /chat-events. Send "status": "done" if the follow-up already happened.
βοΈ POST /workflow-runs: n8n, Make and Zapier
Most people connect n8n or Make with an API key instead (see above). Use this endpoint for Zapier, self-built pipelines, or to push runs yourself. Send one run or an array of up to 500.
| Field | Type | Description |
|---|---|---|
workflow_id | string, required | Your workflow or scenario ID. |
execution_id | string, required | Unique ID of this run (duplicates are ignored). |
status | string, required | success, error, warning, running, waiting, cancelled or crashed. |
platform | string | n8n, make, zapier or other (default). |
workflow_name | string | Readable name shown in alerts. |
started_at | ISO 8601 | When the run started, with a time zone, e.g. 2026-09-29T10:15:00Z. |
duration_ms | number | How long it took. |
output_items | integer | How many items it produced. An empty "success" is flagged when it's unusual for this workflow (ProofMyAI learns its normal quiet spells). |
input_items | integer | How many items it received, e.g. new orders fetched. 0 in and 0 out is a quiet run (no alert); items in but 0 out is a high alert. |
error_message | string | The error, if any. |
retries | integer | Optional: extra attempts the run needed (e.g. a node that succeeded on its 3rd try). Workflows that need retries in 20%+ of runs get a RETRY_RATE_HIGH alert, because a success that needed retries hides a flaky step. |
retry_of | string | Optional: the execution this run retries. n8n's own "Retry execution" re-runs are detected automatically. |
curl -X POST https://proofmyai.com/api/v1/workflow-runs \
-H "Authorization: Bearer $PROOFMYAI_KEY" \
-H "Content-Type: application/json" \
-d '[{"platform": "zapier", "workflow_id": "order-sync", "workflow_name": "Shopify β Sheets",
"execution_id": "ex_99121", "status": "success", "output_items": 0, "duration_ms": 2400}]'Response 201: {"received": 1, "created": 1, "duplicates": 0}
β οΈ Errors & limits
| Status | Meaning |
|---|---|
400 | Invalid JSON or a missing/invalid field. The error field says which one. |
401 | Missing or wrong API key, or the account is suspended. |
402 | Your plan's monthly limit is reached. Upgrade under Plan & billing; nothing else breaks. |
409 | An agent run with this run_id was already recorded. |
413 | Body over 2 MB, or more than 500 workflow runs in one request. |
429 | More than 600 requests per minute for one project. Retry after a short pause. |
Errors look like {"error": "conversation_id: Required"}. A service health check is available at https://proofmyai.com/api/health, and live status at /status.
π Privacy & data
Emails, phone numbers, card numbers, IBANs, IP addresses and names are masked before storage or AI checks. Per project you can turn on results-only storage, set auto-delete, add your own words to mask, or switch AI checking off. Details in the Trust Center.
Stuck? Open a support ticket or message us on WhatsApp. See what's new in the changelog.