Buddy REST API

Use the API to ask your agents questions from your own backend, sync conversations and leads into your CRM or helpdesk, and keep each agent's knowledge up to date whenever your content changes. It's a JSON-over-HTTPS REST API, included in the Pro plan.

Base URL
https://buddy-api.taskgo.ai/api/v1
Machine-readable spec
OpenAPI 3.1 at /openapi.json. Import it into Postman or generate a client.

Quick start

  1. Go to Settings → API keys, create a key with the scopes you need, and copy it. It's shown only once.
  2. Store it server-side, e.g. export BUDDY_API_KEY=sk_live_…. Never put it in browser or mobile code.
  3. Make your first call:
curl "https://buddy-api.taskgo.ai/api/v1/me" \
  -H "Authorization: Bearer $BUDDY_API_KEY"

Authentication

Every request needs a workspace API key in the Authorization header, as a Bearer token. Keys start with sk_live_. Buddy stores only a hash, so a lost key can't be recovered; revoke it and create a new one. Keys belong to the workspace, not to a person, so they keep working when team members leave. API keys work only on /api/v1, not on the dashboard's internal endpoints. If the workspace drops below the Pro plan, its keys stop working (402) until it upgrades again.

Authorization: Bearer sk_live_2Jx…

Scopes

Give each key only the scopes its integration needs. For example, a CRM sync might need only conversations:read and leads:read. A call to an endpoint outside the key's scopes returns 403 insufficient_scope. Team, billing and API-key management aren't available through the API at all.

agents:readList agents and read their public configuration
chat:writeSend messages to an agent and receive AI answers (counts toward your message quota)
conversations:readList conversations and read transcripts
conversations:writeReply as a team member, resolve, reopen and tag conversations
leads:readList contact-form submissions (leads)
knowledge:readList knowledge sources and their sync status
knowledge:writeAdd, re-sync and delete knowledge sources and Q&A answers
analytics:readRead analytics overview metrics
usage:readRead plan, limits and current-period usage
webhooks:writeList, create, update, test and delete webhook endpoints and read their delivery log
privacy:writeFind, export and permanently delete one end user's data (data-subject requests)

Rate limits

Requests are limited per workspace over a sliding 60-second window, counting all keys together. On Pro the limit is 300 requests per minute. Every response tells you where you stand; when you go over, you get 429 rate_limited with a Retry-After header. Rejected requests don't count toward the window. Chat calls also count against your monthly message quota.

X-RateLimit-LimitRequests allowed per minute
X-RateLimit-RemainingRequests left in the current window
X-RateLimit-ResetSeconds until the next request slot frees up
Retry-AfterOn 429 only: seconds to wait before retrying

Pagination

List endpoints take limit (1–100, default 20) and offset. They return the page in data, plus total and has_more. To fetch the next page, add limit to offset and repeat until has_more is false.

{
  "data": [ … ],
  "total": 134,
  "limit": 20,
  "offset": 40,
  "has_more": true
}

Errors

Every error uses the same envelope. Include request_id (also in the X-Request-Id header) when you contact support.

{
  "error": {
    "code": "insufficient_scope",
    "message": "This API key is missing the 'leads:read' scope",
    "request_id": "7f3a9c1e2b4d5a60"
  }
}
StatusCodeMeaning
400bad_requestThe request is invalid, e.g. a website source without a url
401unauthorizedThe key is missing, malformed or revoked
402plan_limit_reachedYour plan doesn't include the API, or you've used up a quota
403insufficient_scopeThe key doesn't have the scope this endpoint needs
403workspace_suspendedThe workspace is suspended
404not_foundThe resource doesn't exist in this workspace
409agent_pausedThe agent is paused and won't answer
422validation_errorThe body or query failed validation; message names the field
409too_many_webhooksThe workspace already has 20 webhook endpoints
422invalid_webhook_urlThe webhook URL isn't https:// or doesn't resolve to a public address
422invalid_identifierPrivacy requests need exactly one of email, anon_id or external_id
429rate_limitedToo many requests; wait Retry-After seconds
429too_many_streamsThe agent is answering too many conversations at once; wait Retry-After seconds
502llm_error / timeoutThe AI model failed or timed out; safe to retry

Audit trail

Changes made through the API, such as adding or deleting knowledge sources and updating conversations, are written to the workspace audit log with the key's name as the actor. Creating and revoking keys is logged too. Admins can filter and export the log from Settings → Audit log.

Webhooks

Buddy can POST an event to your HTTPS endpoint when something happens in your workspace. Add endpoints in Settings → Webhooks or through the API. A 2xx response acknowledges delivery. Anything else, or no answer within 10 seconds, is retried with backoff up to 6 times over about 6 hours. An endpoint that fails 50 times in a row is disabled.

EventWhen
conversation.createdA visitor (widget, hosted page) or the API started a conversation
data: conversation_id, channel, visitor_id, page_url, country, status, created_at
message.createdThe AI agent replied; includes confidence, intent and needs_human
data: message_id, conversation_id, channel, role, content, confidence, intent, needs_human, fallback, citations, created_at
handoff.requestedA conversation needs a human: the AI escalated, or the visitor asked or left their details
data: handoff_id, conversation_id, summary, urgency, source (ai · visitor · lead_form), created_at
lead.createdA visitor submitted the contact form
data: lead_id, conversation_id, name, email, phone, custom, created_at
conversation.resolvedA team member (or the API) resolved a conversation
data: conversation_id, channel, status, resolved_at, resolved_by_user_id
source.completedA knowledge source finished syncing
data: source_id, name, type, stats, finished_at
source.failedA knowledge source failed to sync
data: source_id, name, type, error, finished_at

Every event uses the same envelope:

{
  "id": "0b9d6c1e-…",
  "type": "message.created",
  "created_at": "2026-09-27T10:04:12.511Z",
  "workspace_id": "4c1e…",
  "agent_id": "a7d0…",
  "data": {
    "message_id": 88213,
    "conversation_id": "5f0c…",
    "channel": "widget",
    "role": "assistant",
    "content": "Yes, we ship to Canada in 5–7 business days.",
    "confidence": 0.91,
    "intent": "shipping",
    "needs_human": false,
    "fallback": false,
    "citations": [],
    "created_at": "2026-09-27T10:04:12.498Z"
  }
}

Verify every request. The Buddy-Signature header is t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of `${t}.${rawBody}` with the endpoint's signing secret. Compare in constant time, and reject timestamps older than 5 minutes to stop replays. Deduplicate on the event id, because a retry can deliver the same event twice.

import crypto from "node:crypto";

// Use the RAW request body (a Buffer), not re-serialised JSON.
export function verifyBuddy(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false; // replay protection
  const expected = crypto.createHmac("sha256", secret).update(`${t}.`).update(rawBody).digest("hex");
  const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
import hashlib, hmac, time

def verify_buddy(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", 0))
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Account

Check a key and read your plan and usage.

Inspect the current key

GET/api/v1/meany valid key

Returns the workspace this key belongs to, its scopes and your rate limit. Any valid key can call it, which makes it a good first request.

Request
curl "https://buddy-api.taskgo.ai/api/v1/me" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "workspace": {
    "id": "4c1e…",
    "name": "Acme",
    "slug": "acme"
  },
  "plan": "pro",
  "api_key": {
    "id": "9b2f…",
    "name": "CRM sync",
    "prefix": "sk_live_Ab3x",
    "scopes": [
      "conversations:read",
      "leads:read"
    ]
  },
  "rate_limit": {
    "requests_per_minute": 300
  }
}

Plan, limits and usage

GET/api/v1/usageusage:read

Read-only billing information for the current period: messages used against your quota, agents, and plan limits. Changing plans still happens in the dashboard.

  • A limit of -1 means unlimited.
Request
curl "https://buddy-api.taskgo.ai/api/v1/usage" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "plan": "pro",
  "status": "active",
  "trial_ends_at": null,
  "period_start": "2026-09-01T00:00:00+00:00",
  "period_end": "2026-10-01T00:00:00Z",
  "messages": {
    "used": 1240,
    "chat": 1100,
    "email": 140,
    "included": 3000,
    "overage": 0
  },
  "agents": {
    "used": 2,
    "limit": -1
  },
  "chars_indexed": 812345,
  "limits": {
    "pages": 3000,
    "files": 500,
    "chars": 50000000,
    "mailboxes": 3,
    "team_members": 10,
    "api_requests_per_minute": 300
  }
}

Agents

The AI agents in your workspace.

List agents

GET/api/v1/agentsagents:read

All agents that haven't been deleted, oldest first.

Query parameters

limitinteger
1–100, default 20
offsetinteger
Items to skip, default 0
Request
curl "https://buddy-api.taskgo.ai/api/v1/agents?limit=20" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "a7d0…",
      "name": "Support",
      "slug": "acme-support",
      "status": "live",
      "public_key": "pk_live_…",
      "model_tier": "auto",
      "hosted_url": "https://app.example.com/a/acme-support",
      "created_at": "2026-08-02T09:14:00Z",
      "updated_at": "2026-09-20T17:02:11Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0,
  "has_more": false
}

Get an agent

GET/api/v1/agents/{agent_id}agents:read

One agent, in the same shape as the list items.

Path parameters

agent_iduuidrequired
Agent id (from List agents)
Request
curl "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200

Same shape as the related list endpoint.

Chat

Ask an agent from your own backend, app or bot. It uses the same retrieval, tools and handoff logic as the website widget.

Ask an agent

POST/api/v1/agents/{agent_id}/messageschat:write

Sends one user message and returns the agent's answer. Leave out conversation_id to start a new conversation, then pass the returned conversation_id to continue it. Every call counts as one message against your monthly quota.

Path parameters

agent_iduuidrequired
Agent id (from List agents)

Body (JSON)

messagestringrequired
The user's message, up to 2,000 characters
conversation_iduuid
Continue an earlier API conversation
userobject
{ id, name?, email? } — your id for the end user. Their conversations are grouped under one visitor.
streamboolean
true returns Server-Sent Events (message_start, delta, citation, action, tool_call, handoff, lead_form, message_end, error)
  • Paused agents return 409 agent_paused.
  • When the message quota is used up (and your plan has no overage) the call returns 402 plan_limit_reached.
  • Only conversations created through the API can be continued.
  • Each agent answers up to 20 conversations at the same time (widget and API combined). Beyond that the call returns 429 too_many_streams with Retry-After.
  • When the workspace reaches its daily AI spend ceiling, the agent replies with its fallback message and contact form until midnight UTC, and fallback is true in the message.created webhook.
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/messages" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"message":"Do you ship to Canada?","user":{"id":"cust_1042","email":"[email protected]"}}'
Response · 200
{
  "conversation_id": "5f0c…",
  "message_id": 88213,
  "answer": "Yes — we ship to Canada in 5–7 business days…",
  "citations": [
    {
      "title": "Shipping policy",
      "url": "https://acme.com/shipping"
    }
  ],
  "actions": [],
  "tool_calls": [],
  "confidence": 0.91,
  "needs_human": false,
  "handoff": null,
  "lead_form": null
}

Conversations

Transcripts from every channel: widget, hosted page, email and API.

List conversations

GET/api/v1/conversationsconversations:read

Most recent activity first. To sync incrementally, poll with updated_since set to the time of your last sync.

Query parameters

agent_iduuid
Only this agent
statusstring
open · ai_handled · needs_human · human_active · resolved
channelstring
widget · hosted · email · api · playground
updated_sincedatetime
ISO 8601; last message at or after this time
limitinteger
1–100, default 20
offsetinteger
Items to skip, default 0
Request
curl "https://buddy-api.taskgo.ai/api/v1/conversations?limit=20" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "5f0c…",
      "agent_id": "a7d0…",
      "agent_name": "Support",
      "channel": "widget",
      "status": "resolved",
      "title": "Do you ship to Canada?",
      "tags": [
        "shipping"
      ],
      "sentiment": null,
      "country": "CA",
      "page_url_first": "https://acme.com/",
      "visitor": {
        "name": "Sam",
        "email": "[email protected]",
        "verified": true
      },
      "message_count": 4,
      "last_message_at": "2026-09-26T10:04:00Z",
      "created_at": "2026-09-26T10:01:00Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0,
  "has_more": false
}

Get a transcript

GET/api/v1/conversations/{conversation_id}conversations:read

The conversation plus every message, plus any handoffs and leads captured in it.

Path parameters

conversation_iduuidrequired
Conversation id
Request
curl "https://buddy-api.taskgo.ai/api/v1/conversations/{conversation_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "id": "5f0c…",
  "status": "resolved",
  "messages": [
    {
      "id": 88212,
      "role": "user",
      "content": "Do you ship to Canada?",
      "created_at": "…"
    },
    {
      "id": 88213,
      "role": "assistant",
      "content": "Yes — …",
      "confidence": 0.91,
      "citations": [],
      "created_at": "…"
    }
  ],
  "handoffs": [],
  "leads": []
}

Update status or tags

PATCH/api/v1/conversations/{conversation_id}conversations:write

Resolve or reopen a conversation, or replace its tags. Resolving also closes any open handoff. The change is recorded in the audit log.

Path parameters

conversation_iduuidrequired
Conversation id

Body (JSON)

statusstring
open · resolved · needs_human · human_active
tagsstring[]
Replaces the tag list (max 20)
Request
curl -X PATCH "https://buddy-api.taskgo.ai/api/v1/conversations/{conversation_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"resolved","tags":["billing"]}'
Response · 200
{
  "id": "5f0c…",
  "status": "resolved",
  "tags": [
    "billing"
  ]
}

Reply as a team member

POST/api/v1/conversations/{conversation_id}/messagesconversations:write

Adds a human-agent message. Widget visitors see it on their next poll; email conversations are sent by the email worker.

Path parameters

conversation_iduuidrequired
Conversation id

Body (JSON)

contentstringrequired
Up to 8,000 characters
resolveboolean
Resolve the conversation after replying (default false)
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/conversations/{conversation_id}/messages" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content":"I've issued the refund — you'll see it in 3–5 days.","resolve":true}'
Response · 201
{
  "id": 88240,
  "status": "resolved"
}

Leads

Contact-form submissions collected by your agents.

List leads

GET/api/v1/leadsleads:read

Newest first. For real-time delivery, use the lead webhook on the agent's Contact form tab instead of polling.

Query parameters

agent_iduuid
Only this agent
qstring
Search name, email, phone and custom fields
created_sincedatetime
ISO 8601
limitinteger
1–100, default 20
offsetinteger
Items to skip, default 0
Request
curl "https://buddy-api.taskgo.ai/api/v1/leads?limit=20" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "c21a…",
      "conversation_id": "5f0c…",
      "agent_id": "a7d0…",
      "name": "Sam Lee",
      "email": "[email protected]",
      "phone": "+1 555 0100",
      "custom": {
        "company": "Northwind"
      },
      "created_at": "2026-09-26T10:03:00Z",
      "delivery_status": "delivered",
      "delivery_attempts": 1,
      "delivery_error": null,
      "delivered_at": "2026-09-26T10:03:01Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0,
  "has_more": false
}

Knowledge

What an agent knows. Adding or re-syncing a source starts ingestion in the background.

List sources

GET/api/v1/agents/{agent_id}/sourcesknowledge:read

Every source with its status (pending · processing · ready · error) and indexing stats.

Path parameters

agent_iduuidrequired
Agent id (from List agents)
Request
curl "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/sources" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "e9a1…",
      "type": "website",
      "name": "acme.com",
      "config": {
        "url": "https://acme.com",
        "max_pages": 200
      },
      "status": "ready",
      "stats": {
        "fetched": 184
      },
      "error": null,
      "pinned": false,
      "enabled": true,
      "schedule": "weekly",
      "last_run_at": "2026-09-25T02:00:00Z",
      "created_at": "2026-08-02T09:15:00Z"
    }
  ]
}

Add a source

POST/api/v1/agents/{agent_id}/sourcesknowledge:write

Queues a website crawl, a single URL or a block of text for ingestion. The response comes back straight away with status pending; poll List sources to follow progress. File uploads are dashboard-only for now.

Path parameters

agent_iduuidrequired
Agent id (from List agents)

Body (JSON)

typestringrequired
website · url · text
namestringrequired
Display name
urlstring
Required for website and url
textstring
Required for text; counts against your plan's character limit
max_pagesinteger
website only, default 100; capped by your plan
schedulestring
manual · daily · weekly (default). Daily needs a plan that includes it.
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/sources" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"url","name":"Returns policy","url":"https://acme.com/returns","schedule":"weekly"}'
Response · 202

Same shape as the related list endpoint.

Re-sync a source

POST/api/v1/agents/{agent_id}/sources/{source_id}/syncknowledge:write

Re-crawls or re-reads a source now, for example right after you publish new docs.

Path parameters

agent_iduuidrequired
Agent id (from List agents)
source_iduuidrequired
Source id
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/sources/{source_id}/sync" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 202

Same shape as the related list endpoint.

Delete a source

DELETE/api/v1/agents/{agent_id}/sources/{source_id}knowledge:write

Removes the source and everything indexed from it.

Path parameters

agent_iduuidrequired
Agent id (from List agents)
source_iduuidrequired
Source id
Request
curl -X DELETE "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/sources/{source_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 204

No content.

Add a Q&A answer

POST/api/v1/agents/{agent_id}/answersknowledge:write

A curated question and answer that the agent treats as high-precision knowledge.

Path parameters

agent_iduuidrequired
Agent id (from List agents)

Body (JSON)

questionstringrequired
Up to 2,000 characters
answerstringrequired
Up to 8,000 characters
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/agents/{agent_id}/answers" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Do you offer student discounts?","answer":"Yes — 20% off with a valid .edu email."}'
Response · 201
{
  "id": "1d7e…"
}

Analytics

Headline metrics, the same numbers as the Analytics page.

Overview

GET/api/v1/analytics/overviewanalytics:read

Message and conversation counts, deflection rate, CSAT, confidence, latency, per-day volume, top intents and knowledge gaps.

Query parameters

daysinteger
1–365, default 30
agent_iduuid
Only this agent
Request
curl "https://buddy-api.taskgo.ai/api/v1/analytics/overview?days=…" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "days": 30,
  "messages": 1100,
  "conversations": 402,
  "handoffs": 31,
  "deflection_rate": 0.923,
  "avg_confidence": 0.84,
  "csat": 0.9,
  "feedback": {
    "up": 45,
    "down": 5
  },
  "avg_latency_ms": 2140,
  "navigation_actions": 57,
  "per_day": [
    {
      "date": "2026-09-25",
      "messages": 41
    }
  ],
  "top_intents": [
    {
      "intent": "shipping",
      "count": 120
    }
  ],
  "knowledge_gaps": []
}

Webhooks

Get a signed HTTPS POST when something happens: new conversations, AI replies, hand-offs, leads, resolutions and knowledge syncs. You can also manage endpoints in Settings → Webhooks.

List endpoints

GET/api/v1/webhookswebhooks:write

Your webhook endpoints plus the catalogue of event types you can subscribe to.

Request
curl "https://buddy-api.taskgo.ai/api/v1/webhooks" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "7e21…",
      "url": "https://hooks.acme.com/buddy",
      "events": [
        "lead.created",
        "handoff.requested"
      ],
      "description": "CRM sync",
      "enabled": true,
      "disabled_reason": null,
      "consecutive_failures": 0,
      "last_delivery_at": "2026-09-27T10:04:13Z",
      "last_status_code": 200,
      "created_at": "2026-09-20T08:00:00Z",
      "updated_at": "2026-09-27T10:04:13Z"
    }
  ],
  "event_types": [
    {
      "type": "conversation.created",
      "description": "A visitor (widget, hosted page) or the API started a conversation"
    },
    {
      "type": "message.created",
      "description": "The AI agent replied; includes confidence, intent and needs_human"
    }
  ]
}

Create an endpoint

POST/api/v1/webhookswebhooks:write

Registers a URL for the events you pick. The response includes the signing secret. It's shown only this once, so store it straight away. A workspace can have up to 20 endpoints.

Body (JSON)

urlstringrequired
https:// URL on a public host, up to 500 characters
eventsstring[]required
Event types, or ["*"] for all: conversation.created · message.created · handoff.requested · lead.created · conversation.resolved · source.completed · source.failed
descriptionstring
Up to 300 characters
enabledboolean
Default true
  • Payload envelope: { id, type, created_at, workspace_id, agent_id, data }. The id is stable across retries and redeliveries; use it (or the Buddy-Event-Id header) to de-duplicate.
  • Headers: Buddy-Event, Buddy-Event-Id, Buddy-Delivery and Buddy-Signature: t=<unix seconds>,v1=<hex>. To verify, compute HMAC-SHA256 with your endpoint secret over "<t>.<raw request body>", compare it to v1 in constant time, and reject timestamps older than 5 minutes.
  • Return any 2xx within 10 seconds. Anything else is retried up to 6 attempts over about 6 hours (after 1 min, 5 min, 30 min, 2 h and 3.5 h). After 50 consecutive failed attempts the endpoint is disabled and the change is written to the audit log; re-enable it to reset the count.
  • conversation.created: A visitor (widget, hosted page) or the API started a conversation. data: conversation_id, channel, visitor_id, page_url, country, status, created_at.
  • message.created: The AI agent replied; includes confidence, intent and needs_human. data: message_id, conversation_id, channel, role, content, confidence, intent, needs_human, fallback, citations, created_at.
  • handoff.requested: A conversation needs a human: the AI escalated, or the visitor asked or left their details. data: handoff_id, conversation_id, summary, urgency, source (ai · visitor · lead_form), created_at.
  • lead.created: A visitor submitted the contact form. data: lead_id, conversation_id, name, email, phone, custom, created_at.
  • conversation.resolved: A team member (or the API) resolved a conversation. data: conversation_id, channel, status, resolved_at, resolved_by_user_id.
  • source.completed: A knowledge source finished syncing. data: source_id, name, type, stats, finished_at.
  • source.failed: A knowledge source failed to sync. data: source_id, name, type, error, finished_at.
  • Endpoints must use https:// and resolve to a public address; private, loopback and link-local targets are rejected when you save and again before every delivery.
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/webhooks" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.acme.com/buddy","events":["lead.created","handoff.requested"],"description":"CRM sync"}'
Response · 201
{
  "id": "7e21…",
  "url": "https://hooks.acme.com/buddy",
  "events": [
    "lead.created",
    "handoff.requested"
  ],
  "description": "CRM sync",
  "enabled": true,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivery_at": "2026-09-27T10:04:13Z",
  "last_status_code": 200,
  "created_at": "2026-09-20T08:00:00Z",
  "updated_at": "2026-09-27T10:04:13Z",
  "secret": "whsec_2nq…"
}

Get an endpoint

GET/api/v1/webhooks/{webhook_id}webhooks:write

One endpoint, in the same shape as the list items. The secret is never returned again.

Path parameters

webhook_iduuidrequired
Endpoint id
Request
curl "https://buddy-api.taskgo.ai/api/v1/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200

Same shape as the related list endpoint.

Update an endpoint

PATCH/api/v1/webhooks/{webhook_id}webhooks:write

Change the URL, events or description, or enable and disable the endpoint. Re-enabling an endpoint that was disabled automatically resets its failure count.

Path parameters

webhook_iduuidrequired
Endpoint id

Body (JSON)

urlstring
New https:// URL
eventsstring[]
Replaces the subscribed events
descriptionstring
Up to 300 characters
enabledboolean
false pauses deliveries
Request
curl -X PATCH "https://buddy-api.taskgo.ai/api/v1/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"events":["*"]}'
Response · 200
{
  "id": "7e21…",
  "url": "https://hooks.acme.com/buddy",
  "events": [
    "*"
  ],
  "description": "CRM sync",
  "enabled": true,
  "disabled_reason": null,
  "consecutive_failures": 0,
  "last_delivery_at": "2026-09-27T10:04:13Z",
  "last_status_code": 200,
  "created_at": "2026-09-20T08:00:00Z",
  "updated_at": "2026-09-27T10:04:13Z"
}

Delete an endpoint

DELETE/api/v1/webhooks/{webhook_id}webhooks:write

Removes the endpoint and its delivery log.

Path parameters

webhook_iduuidrequired
Endpoint id
Request
curl -X DELETE "https://buddy-api.taskgo.ai/api/v1/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 204

No content.

Send a test event

POST/api/v1/webhooks/{webhook_id}/testwebhooks:write

Sends a signed webhook.test event right away and returns the delivery result. Test deliveries are never retried and don't count toward automatic disabling.

Path parameters

webhook_iduuidrequired
Endpoint id
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/webhooks/{webhook_id}/test" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "id": "d41c…",
  "webhook_id": "7e21…",
  "url": null,
  "event_id": "0b9d…",
  "event_type": "webhook.test",
  "status": "succeeded",
  "attempts": 1,
  "status_code": 200,
  "latency_ms": 182,
  "error": null,
  "response_snippet": "ok",
  "manual": true,
  "next_attempt_at": null,
  "created_at": "…",
  "updated_at": "…"
}

Delivery log

GET/api/v1/webhooks/{webhook_id}/deliverieswebhooks:write

Newest first. Each delivery keeps its latest attempt: HTTP status, latency, error and the first 500 characters of your response.

Path parameters

webhook_iduuidrequired
Endpoint id

Query parameters

statusstring
pending · succeeded · failed
limitinteger
1–100, default 20
offsetinteger
Items to skip, default 0
Request
curl "https://buddy-api.taskgo.ai/api/v1/webhooks/{webhook_id}/deliveries?limit=20" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "d41c…",
      "webhook_id": "7e21…",
      "url": "https://hooks.acme.com/buddy",
      "event_id": "0b9d…",
      "event_type": "lead.created",
      "status": "failed",
      "attempts": 6,
      "status_code": 503,
      "latency_ms": 10004,
      "error": "HTTP 503",
      "response_snippet": "Service Unavailable",
      "manual": false,
      "next_attempt_at": null,
      "created_at": "…",
      "updated_at": "…"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0,
  "has_more": false
}

Redeliver an event

POST/api/v1/webhooks/deliveries/{delivery_id}/redeliverwebhooks:write

Queues the same event, with the same event id, to the same endpoint again. Delivery starts within a few seconds.

Path parameters

delivery_iduuidrequired
Delivery id from the log
Request
curl -X POST "https://buddy-api.taskgo.ai/api/v1/webhooks/deliveries/{delivery_id}/redeliver" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 202

Same shape as the related list endpoint.

Privacy

Data-subject requests: find, export or permanently delete everything stored about one end user. Pass exactly one identifier.

Find a person

GET/api/v1/privacy/visitorsprivacy:write

Matches visitors by email, anonymous id or verified external id, plus conversations where a contact form carried that email. Returns the visitors and how much data exists.

Query parameters

emailstring
Visitor email or contact-form (lead) email
anon_idstring
The widget's anonymous visitor id; API users are api:<user.id>
external_idstring
Verified external user id from signed identity, or the user.id you sent to Ask an agent
Request
curl "https://buddy-api.taskgo.ai/api/v1/privacy/visitors?email=…" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "match": {
    "type": "email",
    "value": "[email protected]"
  },
  "found": true,
  "visitors": [
    {
      "id": "91fe…",
      "agent_id": "a7d0…",
      "anon_id": "v_1x9k…",
      "external_user_id": null,
      "email": "[email protected]",
      "name": "Sam",
      "verified": false,
      "attributes": {},
      "first_seen_at": "…",
      "last_seen_at": "…"
    }
  ],
  "counts": {
    "visitors": 1,
    "conversations": 2,
    "messages": 14,
    "leads": 1,
    "handoffs": 1
  }
}

Export a person's data

GET/api/v1/privacy/exportprivacy:write

A JSON file with the visitor records, every conversation with its messages and hand-offs, and their leads. The export is written to the audit log with a fingerprint of the identifier, never the identifier itself.

Query parameters

emailstring
Visitor email or contact-form (lead) email
anon_idstring
The widget's anonymous visitor id; API users are api:<user.id>
external_idstring
Verified external user id from signed identity, or the user.id you sent to Ask an agent
Request
curl "https://buddy-api.taskgo.ai/api/v1/privacy/export?email=…" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200

Same shape as the related list endpoint.

Delete a person's data

DELETE/api/v1/privacy/visitorsprivacy:write

Permanently deletes the person's messages, conversations, hand-offs, leads and visitor records, plus queued webhook events about those conversations. This can't be undone. The deletion is audited and the response gives counts per table.

Query parameters

emailstring
Visitor email or contact-form (lead) email
anon_idstring
The widget's anonymous visitor id; API users are api:<user.id>
external_idstring
Verified external user id from signed identity, or the user.id you sent to Ask an agent
  • Email conversations imported from Gmail or Outlook aren't matched by this endpoint. Remove those threads in your mailbox and in the inbox.
Request
curl -X DELETE "https://buddy-api.taskgo.ai/api/v1/privacy/visitors?email=…" \
  -H "Authorization: Bearer $BUDDY_API_KEY"
Response · 200
{
  "deleted": {
    "messages": 14,
    "handoffs": 1,
    "leads": 1,
    "conversations": 2,
    "visitors": 1,
    "webhook_events": 0,
    "integration_calls": 0
  }
}