Subscribe to message_posted; format and forward to a Slack incoming-webhook.
Drive the chat from your code.
Pre-authenticate visitors so contacts are known, open and close the widget on demand, and stream support events to your stack — all from your existing JavaScript and server.
Install
Paste this once in your site's <head>. Everything else is optional.
<!-- Paste once, just before </head> --> <script async src="https://your-helpbird-host/api/embed?id=YOUR_PROJECT_ID"></script>
Replace YOUR_PROJECT_ID with your own project ID from Settings → Install. The async loader injects the chat button and iframe (it never blocks your page) and exposes a global window.HelpBird object. If the project is inactive or the widget is hidden, the script is an empty no-op — a stale tag never breaks the host page.
HelpBird.identify()
Tell the widget who the visitor is so the conversation lands on a known contact — the email matches or creates the contact and attaches their cross-conversation history.
Signature
HelpBird.identify({ email, name?, user_id?, hmac? })Parameters
user_id— your own identifier for the visitor. optionalemail— used to match or create the contact and build their history. requiredname— display name shown to your agents. optionalhmac— server-signed digest for verified identity. optional
Safe to call any time after the loader has run — the widget picks it up even if the visitor hasn't opened the chat yet.
<script>
// Any time after the loader has run — one object:
HelpBird.identify({
email: "jane@acme.com", // required — matches/creates the contact
name: "Jane Doe", // optional — shown to your agents
user_id: "your-internal-user-42", // optional — your own identifier
hmac: "<hmac from your server>", // optional — verified identity
});
</script>open() & close()
Show or hide the chat widget from your own UI — wire it to a "Contact support" button, a help menu, or an exit-intent flow.
Methods
HelpBird.open()— open the widget panel.HelpBird.close()— collapse the widget back to the launcher.
Both are safe to call any time after the loader has run.
<button onclick="HelpBird.open()">Contact support</button>
Verified identity
Pre-authenticate your visitors with your existing login system. Sign the visitor's identifier server-side with your project's identity secret, pass the digest to identify, and the contact arrives already known — with a verified badge your agents can trust.
How it works
- The digest is
HMAC-SHA256(secret, identifier), hex-encoded. The identifier is theuser_idyou pass toidentify(), or the email if you don't pass one. - The secret is in Settings → Install → Identity verification (account owner, Pro). Keep it server-side — never ship it in page JavaScript. Rotating it there invalidates every signature made with the old one.
- On a valid signature the conversation is marked verified, so agents know exactly who they're helping.
Why use it
- Visitors never re-type their name or email — the contact is known from the first message.
- Only signed calls get the Verified badge. Unsigned calls still work but aren't marked verified, so agents can tell the difference.
- Conversation history attaches to the right contact every time.
// Sign once per page load, server-side.
import { createHmac } from "node:crypto";
// Identifier: your user_id if you pass one to identify(), else the email.
const hmac = createHmac("sha256", process.env.HELPBIRD_IDENTITY_SECRET)
.update(String(user.id))
.digest("hex");
// Render it into the page, then:
// HelpBird.identify({ user_id: String(user.id), email: user.email, name: user.fullName, hmac });Webhooks
Subscribe to support events from outside the browser — Slack notifications, CRM sync, Zapier flows, custom analytics, anything. We POST a signed JSON payload; you verify the signature and process.
Configure
Settings → Webhooks. Add a URL, pick events, and keep the shared secret somewhere safe — it signs every delivery.
Events
message_posted— a new message from a visitor or an agent.mention_received— a teammate is @mentioned in an internal note.data.mentionslists who.
Headers
Content-Type: application/jsonX-Helpbird-Event— the event name (matches the body'seventfield).X-Helpbird-Signature— hex-encoded HMAC-SHA256 over the raw request body, keyed with your shared secret.
Behavior
- Best-effort delivery. We don't queue or retry — design your receiver to be idempotent.
- 6-second timeout per delivery. Slow receivers are recorded as failed and we move on.
- The last HTTP status and error per endpoint are visible in the admin panel.
Payload
{
"event": "message_posted",
"project_id": 123,
"data": {
"comment_id": 67890,
"room_id": 12345,
"channel": "12345",
"username": "Jane Doe",
"message": "Hi, my order shipped to the wrong address.",
"is_admin": false,
"mentions": []
}
}Verifying the signature
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: Buffer, headerSig: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
return (
expected.length === headerSig.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(headerSig))
);
}Common patterns
On message_posted, log the message against the conversation in your CRM — room_id is a stable key.
On mention_received, page the on-call lead so an at-risk conversation gets picked up fast.
REST API
For deploy platforms and scripts that give each app its own chat: create a site per app, keep its allowed domains in sync with the app's hosts, read how its inbox is doing, show visitors a notice while the app is in maintenance or down, and record where it runs.
Authentication
Authorization: Bearer hbmcp_… — a token from Settings → AI agents (or an OAuth access token, as for the MCP server). It acts as the account owner and reaches that account's sites only: another account's id is a 404. Revoke it in the same place; changing your password revokes every token.
Conventions
- JSON in and out:
{ data }on success,{ error }(sometimes with acode) on failure. Bodies are JSON objects up to 64 KB. - 120 reads and 60 writes a minute per token; past that,
429withRetry-After. domainsare host names (each covers its subdomains); URLs, ports and*.prefixes are reduced to the host. Up to 20.- A site's
installedAt/lastSeenAtsay when a page first and last loaded its snippet;lastVisitorAt, when a visitor last had the chat open.installState.stateisinstalled,not_detectedorshopify(installed through the Shopify app), as the MCP toolsget_installandcheck_installsay it. installInstructions(one site's answers, not the list) is the text to paste into Claude Code, Cursor or Codex: where the snippet goes for each stack, the rules, how to check it, and how to connect the MCP server to finish the setup — the same text as Settings → Install → “Ask your AI agent”. For a site other than the account's first, it names the site and has the agent passsiteto the MCP tools.- A notice past its
untilis simply no longer shown. Deploy links follow the same contract as Jentry's, one per app bykey.
| Endpoint | Body | What it does |
|---|---|---|
| GET /api/v1/projects | — | Your sites (projects), oldest first, without installInstructions. |
| POST /api/v1/projects | { name, domains? } | Finds your site with that name (any case) or creates one: 201 { data, created: true }, or 200 { data, created: false } when it already existed (returned unchanged). At most 50 sites per account (403, code project_limit). |
| GET /api/v1/projects/:id | — | One site, with the installInstructions to give a coding agent. |
| PATCH /api/v1/projects/:id | { name?, domains? } | Rename it (409, code name_taken, if another site has that name) or replace the hosts where its chat may load ([] = anywhere). |
| GET /api/v1/projects/:id/stats | — | open, unread, waiting, slaBreached, oldestWaitingAt; on plans with analytics also csat7d, csatCount7d and medianFirstReplySeconds7d (else null). |
| GET /api/v1/projects/:id/notice | — | The notice visitors see now, or null. |
| PUT /api/v1/projects/:id/notice | { text, until?, source? } | Show visitors a notice above the conversation until `until` (ISO 8601, optional) or until removed. Plain text, 280 characters. |
| DELETE /api/v1/projects/:id/notice | — | Remove it: { data: { removed } }. |
| GET /api/v1/projects/:id/deploy-links | — | Where the site's code runs. |
| PUT /api/v1/projects/:id/deploy-links | { key, provider, instance, app, appUrl?, consoleUrl? } | Add or replace (by key) one app's link. http(s) URLs only; 20 links at most. |
| DELETE /api/v1/projects/:id/deploy-links?key=… | — | Drop one: { data: { removed } }. |
curl -X POST https://helpbird.app/api/v1/projects \
-H "Authorization: Bearer hbmcp_..." \
-H "Content-Type: application/json" \
-d '{"name": "acme-shop", "domains": ["acme-shop.example.app", "shop.acme.com"]}'
# 201
{
"data": {
"id": 123,
"name": "acme-shop",
"domains": ["acme-shop.example.app", "shop.acme.com"],
"active": true,
"inactiveReason": null,
"platform": "web",
"embedUrl": "https://helpbird.app/api/embed?id=123",
"snippet": "<script async src=\"https://helpbird.app/api/embed?id=123\"></script>",
"panelUrl": "https://helpbird.app/settings?project=123",
"inboxUrl": "https://helpbird.app/inbox?project=123",
"installedAt": null,
"lastSeenAt": null,
"lastVisitorAt": null,
"installState": {
"state": "not_detected",
"detail": "No page has loaded the snippet yet. It is detected the first time a page with it opens."
},
"installInstructions": "Install the HelpBird chat widget on this website: my HelpBird site \"acme-shop\" (project 123), …",
"notice": null,
"deployLinks": [],
"createdAt": "2026-10-21T14:00:00.000Z"
},
"created": true
}MCP server
Claude, ChatGPT, Claude Code, Cursor and any other MCP client can do everything you do in the admin panel: read and answer conversations, assign, tag, merge and close them, keep contacts, saved replies and help-center articles up to date, run automations and campaigns, and change the widget's settings, the team and the plan. They act as the account owner, through the same checks as the admin panel. Start with the diagnose_account tool: what needs attention now, with the call that fixes each thing. Prompts clear_inbox, improve_bot and setup_workspace run whole jobs.
Server
https://helpbird.app/mcp — Streamable HTTP (stateless JSON-RPC). It signs in with OAuth 2.1: the first time, your client opens a HelpBird page where you sign in and click Allow. Nothing to register with Anthropic or OpenAI. Discovery at /.well-known/oauth-protected-resource/mcp, dynamic client registration, PKCE S256; redirects to https, http loopback or Cursor's app callback.
Connect
- claude.ai: Settings → Connectors → Add custom connector, paste the URL.
- ChatGPT: Settings → Apps & Connectors → Advanced settings → Developer mode → Create, paste the URL, OAuth.
- Cursor, VS Code, Windsurf, Codex: add an HTTP MCP server with the URL.
Step-by-step for each client, what an agent can do, and the safety model →
Access
- Only the account owner can connect apps. See and revoke them in Settings → AI agents; changing your password disconnects every app.
- Plan limits apply exactly as in the panel. Tools that reach your customers (a reply, a file, a transcript, a campaign), delete something, send data or access outside your account (webhooks, alert addresses, new teammates, the identity secret) or cancel your plan return a preview and change nothing until called again with
confirm: true; cancelling also needs the price echoed back (amount_confirmed). - No tool moves money:
change_planreturns the link where you change the plan yourself (your Billing page, or Shopify's approval page). - Signing in, passwords, agent tokens, payment cards and deleting the account stay in the panel.
Claude Code
claude mcp add --transport http helpbird https://helpbird.app/mcp # then, inside Claude Code: /mcp
# Clients that can't sign in with OAuth: a token from Settings → AI agents claude mcp add --transport http helpbird https://helpbird.app/mcp \ --header "Authorization: Bearer hbmcp_..."
Panel API
The JSON API behind the admin panel, as the MCP server's panel_api_read, panel_api_create, panel_api_update and panel_api_delete tools reach it, acting as the account owner. These are the only endpoints they accept; each runs the panel's own checks. Writes are previewed until called with confirm: true. Sign-in, passwords, agent tokens, the identity secret, billing changes and uploads are not part of it (they have their own tools, or stay in the panel); no endpoint that pays, bills a card or pays anything back is reachable.
Read (GET) — panel_api_read
Changes nothing. Parameters go in the query string.
| Endpoint | What it does | Query |
|---|---|---|
| GET /api/inbox/list | Conversations, most urgent first (one page). | status=open|closed, q, assignee=mine|unassigned|owner:<id>|subadmin:<id>, unread=1, snoozed=1, tag=<tag id>, sla=breached, project=<site id>, cursor=<id> |
| GET /api/inbox/agents | The owner and the teammates, with presence. | — |
| GET /api/inbox/tags | Conversation tags; with room_id, also the ones on that conversation. | room_id |
| GET /api/inbox/savedviews | Saved inbox views. | — |
| GET /api/inbox/cannedresponses | Saved replies (the first 100, most used first). | — |
| GET /api/inbox/availability | The owner's availability. | — |
| GET /api/inbox/notifications | The owner's mention notifications. | — |
| GET /api/settings/all | Every widget setting, the themes, blocked conversations and webhooks. | — |
| GET /api/settings/webhooks | Outgoing webhooks and their last delivery. | — |
| GET /api/settings/blocked | Blocked conversations. | — |
| GET /api/account/overview | Profile, plan, seat limit and teammates. | — |
| GET /api/account/teammates | Teammates. | — |
| GET /api/account/invoices | Recent invoices (Stripe). | — |
| GET /api/automations/list | Automation rules, tag names and assignable agents. | — |
| GET /api/campaigns/list | Campaigns and the contact tags they can target. | — |
| GET /api/helpcenter/categories | Help-center categories. | — |
| GET /api/helpcenter/articles | Help-center articles and searches without results; with id, one article. | id |
| GET /api/analytics | Support analytics (Pro plan). | days=7|30|90 |
| GET /api/billing/getplan | The plan and subscription status. | — |
| GET /api/shopify/embed-status | Whether the Shopify theme's HelpBird app embed is on. | — |
Create (POST) — panel_api_create
Makes something new. JSON body.
| Endpoint | What it does | Body |
|---|---|---|
| POST /api/inbox/message | Send a reply to the customer, as the owner. | room_id, message |
| POST /api/inbox/note | Add an internal note (@Name mentions a teammate). | room_id, message |
| POST /api/inbox/tags | Create a conversation tag. | action=create, name |
| POST /api/inbox/savedviews | Save an inbox view (overwrites one with the same name). | action=create, name, query (the inbox query string) |
| POST /api/inbox/cannedresponses | Create a saved reply. | action=create, title, body, also_close |
| POST /api/settings/theme_save | Create a color theme. | without edit_theme_id; name, header_background_color, header_font_color, messages_color, font_color, border_color, background_color, chat_color, border_width |
| POST /api/settings/webhook_save | Add an outgoing webhook (its signing secret is hidden in the answer). | without id; url, events (comma-separated), active |
| POST /api/account/teammate | Add a teammate. | without id; email, nickname, password |
| POST /api/automations/create | Create an automation rule. | name, trigger, match=all|any, conditions[], actions[], stop_after, timeout_minutes |
| POST /api/campaigns/create | Draft a campaign. | title, body, target_tag |
| POST /api/campaigns/send | Broadcast a drafted campaign to its contacts, once. | id |
| POST /api/helpcenter/categories | Create a help-center category. | action=create, name |
| POST /api/helpcenter/articles | Create a help-center article. | action=create, title, excerpt, body, category_id, published |
Update (POST) — panel_api_update
Changes something that exists. JSON body.
| Endpoint | What it does | Body |
|---|---|---|
| POST /api/inbox/notifications_read | Mark notifications read (all, or the ids given). | ids[] |
| POST /api/inbox/status | Close or reopen a conversation. | room_id, status=open|closed |
| POST /api/inbox/priority | Set a conversation's priority. | room_id, priority=0|1|2 |
| POST /api/inbox/snooze | Snooze a conversation, or wake it (minutes=0). | room_id, minutes, until_reply |
| POST /api/inbox/block | Block or unblock a conversation. | room_id, blocked=1|0 |
| POST /api/inbox/banip | Add an IP to the banned list. | room_id, ip |
| POST /api/inbox/bulk | Close, reopen or snooze many conversations. | room_ids[], action=close|open|snooze, minutes |
| POST /api/inbox/assign | Assign a conversation. | room_id, kind=owner|subadmin|none, agent_id |
| POST /api/inbox/tags | Put a tag on a conversation. | action=apply, tag_id, room_id |
| POST /api/inbox/tags | Take a tag off a conversation. | action=remove, tag_id, room_id |
| POST /api/inbox/contactnote | Replace the note of a conversation's contact. | room_id, note |
| POST /api/inbox/contactattribute | Set or remove an attribute of a conversation's contact. | room_id, action=set|delete, key, value |
| POST /api/inbox/contacttag | Add or remove a tag of a conversation's contact. | room_id, action=add|remove, name |
| POST /api/inbox/availability | Set the owner's availability. | status=active|away|busy|offline |
| POST /api/settings/save | General, localization, behavior and appearance fields. | id, name, restricted_domain, support_agent_name, language, … (see get_settings) |
| POST /api/settings/hours | Business hours and the away message. | business_hours_enabled, business_days, business_open, business_close, business_timezone, away_message |
| POST /api/settings/greeting | The greeting. | default_message, default_message_delay |
| POST /api/settings/proactive | The proactive message. | welcome_bot_enabled, welcome_message, welcome_delay_seconds, welcome_url_contains, welcome_followup_message, welcome_followup_delay_seconds |
| POST /api/settings/ai | AI auto-reply. | ai_autoreply_enabled, ai_autoreply_prompt, ai_autoreply_daily_cap, ai_summary_on_close |
| POST /api/settings/routing | Auto-assignment and the first-response SLA. | auto_assign_enabled, sla_response_minutes |
| POST /api/settings/moderation | Banned words and IPs, AI moderation, slow mode. | banned_words, banned_ips, enable_ai_moderation, slow_mode_seconds |
| POST /api/settings/notifications | The email digest. | email_notification, email_notification_time, email_notification_account |
| POST /api/settings/alerts | New-conversation alerts (email, Slack). | support_email_alerts, support_alert_emails, slack_webhook_url |
| POST /api/settings/theme_save | Change one of the account's themes. | with edit_theme_id; edit_theme_id, name, colors… |
| POST /api/settings/webhook_save | Change a webhook. | with id; id, url, events, active |
| POST /api/settings/block | Unblock (or block) a conversation from Moderation. | room_id, blocked |
| POST /api/account/profile | The owner's display name, avatar and link (not the login email). | nickname, image, profile_link |
| POST /api/account/teammate | Change a teammate's name, email or password. | with id; id, email, nickname, password |
| POST /api/account/teammate-login | Let teammates sign in to the panel, or not. | enabled |
| POST /api/automations/update | Change an automation rule. | id, name, trigger, match, conditions[], actions[], stop_after, timeout_minutes |
| POST /api/automations/toggle | Switch a rule on or off. | id |
| POST /api/automations/reorder | Set the order rules run in. | order[] (rule ids) |
| POST /api/helpcenter/categories | Rename or reorder a category. | action=update, id, name, position |
| POST /api/helpcenter/articles | Change an article. | action=update, id, title, excerpt, body, category_id, published |
| POST /api/setup/skip | Mark a getting-started step skipped. | step=install|customize|welcome|hours|team |
Delete (POST) — panel_api_delete
Removes something for good. JSON body.
| Endpoint | What it does | Body |
|---|---|---|
| POST /api/inbox/merge | Merge a conversation into another; the source goes away. | source_room_id, target_room_id |
| POST /api/inbox/tags | Delete a conversation tag everywhere. | action=delete, tag_id |
| POST /api/inbox/savedviews | Delete a saved view. | action=delete, id |
| POST /api/inbox/cannedresponses | Delete a saved reply. | action=delete, id |
| POST /api/settings/theme_delete | Delete one of the account's themes. | theme_id |
| POST /api/settings/webhook_delete | Delete a webhook. | id, project_id |
| POST /api/account/teammate-delete | Remove a teammate. | id |
| POST /api/automations/delete | Delete an automation rule. | id |
| POST /api/campaigns/delete | Delete a campaign. | id |
| POST /api/helpcenter/categories | Delete a category (its articles stay). | action=delete, id |
| POST /api/helpcenter/articles | Delete an article. | action=delete, id |