# SendEasy documentation (en) # AI integrations and MCP Connect your AI assistant to the official documentation to build SendEasy integrations using documented endpoints, payloads and authentication. ## Choose an access method | Method | Purpose | Address | | --------------- | ------------------------------------------------------ | ------------------------------------------------------------------------ | | `llms.txt` | Discover relevant documentation | [English index](https://docs.sendeasy.app/en/llms.txt) | | `llms-full.txt` | Read all English documentation at once | [Full English documentation](https://docs.sendeasy.app/en/llms-full.txt) | | Markdown | Read a specific page without interface HTML | [Public API example](https://docs.sendeasy.app/en/server/api-publica.md) | | MCP | Let the assistant search and read references on demand | `https://docs.sendeasy.app/mcp` | Portuguese indexes are at `/llms.txt` and `/llms-full.txt`; Spanish indexes are at `/es/llms.txt` and `/es/llms-full.txt`. Append `.md` to a documentation page URL to read its Markdown version. Home pages use `/index.md`, `/en/index.md` and `/es/index.md`. ## Connect with MCP Add a remote MCP server in a client that supports Streamable HTTP: | Setting | Value | | -------------- | ------------------------------- | | Name | `sendeasy-docs` | | URL | `https://docs.sendeasy.app/mcp` | | Transport | Streamable HTTP | | Authentication | None | Configuration formats vary by client. For clients accepting `mcpServers` entries with a `url`: ```json { "mcpServers": { "sendeasy-docs": { "url": "https://docs.sendeasy.app/mcp" } } } ``` Available tools: | Tool | Usage | | ------------- | ----------------------------------------------------------------------------------------------------------------- | | `search_docs` | Search with `query`, `locale` (`pt`, `en`, `es`) and `limit` (1 to 10). | | `get_doc` | Read the exact `path` returned by search. For long pages, pass the returned `nextOffset` as `offset` to continue. | | `list_docs` | List pages and descriptions in the requested `locale`. | The default language is Portuguese. Set `locale` to `en` for English search results. `get_doc` follows the path language, for example `/en/server/webhooks`. MCP resources also expose indexes at `sendeasy://docs/pt`, `sendeasy://docs/en` and `sendeasy://docs/es`. This MCP server reads public documentation. It does not send messages, modify contacts or access accounts. Do not provide customer tokens to the documentation server. Your integration runs in your own system through the SendEasy API using the appropriate credentials. ## Example prompt ```text Use the official SendEasy documentation to build a Node.js integration that sends a WhatsApp message when a lead submits my form. Search for /api/messages/send and read the full reference in English. Confirm the token type, required fields and phone number format. Use an environment variable for the token, handle errors, and distinguish queue acceptance from final delivery. Cite your sources and do not invent fields. ``` Without MCP, ask the assistant to start at `https://docs.sendeasy.app/en/llms.txt` and follow the relevant Markdown links. ## API authentication The API base URL is `https://backend.sendeasy.app`. Use `Authorization: Bearer YOUR_TOKEN` with the token appropriate to the endpoint: | Operation | Token | Reference | | -------------------- | ------------------- | ------------------------------------------------------------------------- | | `/api/messages/send` | Channel token | [Public API](https://docs.sendeasy.app/en/server/api-publica.md) | | `/api/v1/waba/*` | API Token | [WABA API](https://docs.sendeasy.app/en/server/waba.md) | | `/api/v1/email/*` | Email channel token | [Email API](https://docs.sendeasy.app/en/server/email.md) | | `/api/v1/ai/*` | API Token | [Bots API](https://docs.sendeasy.app/en/server/bots.md) | | `/api/integration/*` | Integration token | [Custom integration](https://docs.sendeasy.app/en/integrations/custom.md) | Keep credentials on your application server. Read the [Technical Reference](https://docs.sendeasy.app/en/server/referencia-tecnica.md) and [webhook formats](https://docs.sendeasy.app/en/server/webhooks.md). An accepted or queued request does not confirm final message delivery. --- # Create and configure AI Bots 1. Open the correct project and select **Bots**. 2. Click **Add**, enter a clear name and write the bot instructions. 3. Define its goal, tone, required customer data, limits and human handoff rules. 4. Select the available model and options, then save. 5. Open the bot to add trusted files and media to its knowledge base. Creating a bot does not activate it. Associate it directly with one or more support stages, or add it as a step in an existing flow and associate that flow with the required stages. If it does not reply, verify that the bot is active, the bot or flow is linked to the correct stage, the flow was saved, the channel is connected and the ticket entered that stage. Sendy can list, create and update bots, manage instructions, files and media, link bots to stages and add a bot to a flow. Activations and deletions require confirmation. Beyond WhatsApp support, the bot can be queried from your own application: see the [Bots API](https://docs.sendeasy.app/en/server/bots.md). --- # Broadcast Campaigns Use campaigns for outbound communication at scale. ## Before launch 1. Validate audience segmentation. 2. Validate templates/content. 3. Configure send windows and rate limits. 4. Define opt-out handling. ## After launch * Track delivery and engagement. * Monitor error rates by provider. * Pause quickly if anomaly is detected. --- # Connect Channels Use this page as a quick checklist to connect channels in your project. ## Basic flow 1. Open your project dashboard. 2. Go to **Connections**. 3. Click **Add Channel**. 4. Select provider/channel type. 5. Complete credentials/verification. 6. Validate status as **Connected**. ## Operational tips * Keep one channel marked as default when required. * Monitor reconnect events and webhook health. * Store provider credentials securely. --- # Create and publish automation flows A flow is an executable automation. It can send messages, wait, call an AI Bot, add tags and move a ticket to another stage. ## Create it manually 1. Open **Flows** and click **New Flow**. 2. Name the flow and add the steps in execution order. 3. To use AI, add an AI Bot step and select an existing bot. 4. Save the flow and associate it with the required support stages. 5. Test with an internal contact before enabling it for customers. You can also describe the complete sequence to Sendy. Sendy must show a full preview and ask for confirmation, then create the real flow. A script or example alone does not mean a flow was created. If a flow does not start, check the stage association and ticket stage. If its bot does not run, verify that the bot step was saved. Outbound steps also require a connected channel. --- # ActiveCampaign Two-way integration with **ActiveCampaign** to sync contacts, lists and trigger automated messages from marketing events. **Documentation in progress.** This page will soon include the configuration walkthrough, list sync, field mapping and common troubleshooting. For now, configure the integration in **Settings → Integrations → ActiveCampaign** inside your Sendeasy account. --- # Booking on your website (embed) With the **embed**, customers pick the service and the time and confirm the booking **without leaving your website**. Paste one line of code on the page: the `embed.js` script mounts booking in a safe `iframe` served by `https://calendar.sendeasy.app`. The fastest way is to generate the code in the dashboard: **Calendar → Public page → Share**. Pick the calendar, the service, the format, the color and the campaign, copy the ready code and check the preview. ## Requirements * Website booking enabled for your company. * A **time slot** calendar with active **event types** (services). Lodging calendars have no embed: use the calendar link. * Your website on **https**. Pages on `http` or opened as a local file cannot show booking. ## Installation Paste the code where booking should show: ```html ``` `YOUR_CALENDAR` is the calendar address, the same as in the link `https://calendar.sendeasy.app/c/YOUR_CALENDAR`. To place booking inside an element of yours (instead of right after the tag), mark the element and load the script once on the page: ```html
``` ## Formats | `data-mode` | What shows | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `inline` (default) | Booking inside the page, right after the tag (or inside the marked element). The height follows the content. | | `popup` | A button where the tag is. Clicking it opens booking in a window over the page. On a marked element that is not a ` ``` * `window.SendeasyCalendar.open(options)` opens the window and returns `true` (or `false` with an invalid calendar). Options: `calendar` (required), `type`, `color`, `lang` and `utm` (`{ source, medium, campaign, term, content }`). * `window.SendeasyCalendar.close()` closes the open window. Since the script loads with `async`, check that `window.SendeasyCalendar` exists before calling it. ## Messages to your website (postMessage v1) Booking notifies the page with `postMessage`. Every message has this shape: ```json { "source": "sendeasy", "v": 1, "type": "..." } ``` | `type` | Fields | When | | -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ready` | | Booking has loaded. | | `resize` | `height` (px, up to 20000) | The content height changed. `embed.js` already adjusts the `inline` format by itself. | | `slot_selected` | `date` (`YYYY-MM-DD`), `time` (`HH:mm`) | The customer picked a time. Local date and time of the business. | | `booking.created` | `code`, `status`, `startAt` | The booking was created. `code` is the 8 character code (or `null`), `status` is `HOLD`, `PENDING_PAYMENT`, `AWAITING_APPROVAL`, `CONFIRMED` or `CANCELLED`, and `startAt` is the start in ISO 8601 (UTC). | | `booking.payment_required` | `code` | The booking needs payment; the payment button opens the checkout in a new tab. | | `closed` | | The customer closed booking (Esc or Close), in the `popup` and `button` formats. | Messages **carry no personal data**: no name, email, phone, answers, payment or management links. Booking **accepts no messages** from your page. Always check the origin before using them: ```js window.addEventListener('message', function (event) { if (event.origin !== 'https://calendar.sendeasy.app') return var data = event.data if (!data || data.source !== 'sendeasy' || data.v !== 1) return if (data.type === 'booking.created') { // Example: conversion event in Google Analytics 4 gtag('event', 'booking_created', { status: data.status }) } }) ``` Messages are only sent to `https` pages whose origin the browser gives to the `iframe`. Unknown origin: no message is sent. ## Your website CSP If your website sends a `Content-Security-Policy` header, allow the calendar domain for the script and for the `iframe`: ```http Content-Security-Policy: script-src 'self' https://calendar.sendeasy.app; frame-src https://calendar.sendeasy.app ``` Add these origins to the directives you already use (`script-src` and `frame-src`, or `child-src` if the site does not use `frame-src`). On the Sendeasy side, the `iframe` can be shown by any `https` website. ### Pinned version with SRI (optional) `/embed.js` gets fixes automatically (cached for up to one day). To pin a version with Subresource Integrity, read the version and the hash from the response headers: ```bash curl -sI https://calendar.sendeasy.app/embed.js | grep -i x-sendeasy-embed ``` and use both values: ```html ``` When the script changes, the old version no longer matches the hash and the browser runs nothing: update both values. ## Limits * **Payment always opens in the top level tab**, in a new tab, outside your website. The checkout and the booking management links can never be shown inside an `iframe`. * `https` websites only. `http` pages do not show booking. * Time slot calendars with event types only. Lodging keeps using the calendar link. * The `iframe` keeps nothing between visits (browsers block third party storage); the confirmation stays on screen until the customer closes it. * The booking page inside the `iframe` is not indexed by search engines. To show up on Google, use your Public page and the [Google Business Profile](https://docs.sendeasy.app/en/integrations/calendario/google-business-profile.md). * One window at a time: opening another closes the previous one. ## Common problems | Symptom | What to check | | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Nothing shows | Is the feature enabled for the company? Is the calendar address right? Does the browser console show a CSP block? | | Error page inside the frame | The calendar has no active services, or the `data-type` service does not exist. | | Booking does not show on `http` | The website must be on `https`. | | Payment opens in another tab | That is expected: the checkout never runs inside your website. | --- # Google Business Profile Your **Google Business Profile** (formerly Google My Business) shows on Search and Maps when someone looks for your business. With two Sendeasy links on the profile, customers book straight from there, and bookings are recorded with the **Google Business Profile** origin. The dashboard generates both links: **Calendar → Public page → Share**, **Google Business Profile** section. The step by step and a checklist are there too. ## The two links | Google field | Sendeasy link | | --------------------------------------- | ------------------------------------------------------------------------------------ | | **Website** | Your Public page, when it is live. Without a Public page, the calendar booking link. | | **Appointment links** (**Book** button) | The calendar booking link, optionally already on one service. | Both always carry the same UTM tags: ```text utm_source=google&utm_medium=business_profile&utm_campaign=gbp ``` Examples: ```text https://calendar.sendeasy.app/p/downtown-clinic?utm_source=google&utm_medium=business_profile&utm_campaign=gbp https://calendar.sendeasy.app/c/clinic?type=consultation-30&utm_source=google&utm_medium=business_profile&utm_campaign=gbp ``` `downtown-clinic` is the Public page address, `clinic` the calendar address and `consultation-30` the service address. For lodging, the calendar link is `https://calendar.sendeasy.app/h/YOUR_CALENDAR`. ## Step by step 1. Go to [business.google.com](https://business.google.com/) (or search for your business name on Google) with the account that manages the profile. 2. Click **Edit profile** and open the **Contact** section. 3. In the **Website** field, paste the Website link and save. 4. In **Appointment links**, add the Book button link and save. If more than one booking provider shows up, set your link as preferred. 5. Wait for the Google review, which can take a few days, and check the **Book** button on the profile. 6. Open both links in a private window and make a test booking. Field names can vary a little with the business category and the country. Some categories have no **Appointment links**: in that case, use only the **Website** field. ## Checklist * [ ] Calendar with a public address and active services. * [ ] Calendar linked to the Public page, and the Public page live (for the Website link). * [ ] Same name, address and phone on Google and on the Public page. * [ ] Links tested in a private window. * [ ] Website and Appointment links saved on Google. ## How the origin is measured Whoever arrives through one of these links and books gets the **Google Business Profile** origin. `utm_medium=business_profile` decides; the other UTM tags are recorded as they came. * Do not change the UTM tags: without `utm_medium=business_profile`, the Google visit counts as organic search. * Visits from an AI assistant, from the Sendeasy directory or from a Google ad (with `gclid`) keep their own origin, even with these UTM tags. * The origin applies to bookings made in the same browser over the next 30 days, until the next visit with another origin. * Only the origin is recorded: no visitor data goes into the URL. ## Limits * The **Website** link only points to the Public page when it is published. Before that, the dashboard uses the booking link. * Google reviews the changes and it can take a few days. * Besides your link, Google can show partner booking providers; Google decides which one shows first. * To book inside your own website, see [Booking on your website (embed)](https://docs.sendeasy.app/en/integrations/calendario/embed.md). --- # Calendar Integration Lets **any application** connect a Google Calendar to a Sendeasy calendar and keep both sides in sync — without going through the console. The connection is made through the **public API** (`/api/v1/calendars/...`), authenticated with the company's **API token**. Once connected, Sendeasy handles **webhooks** and event **synchronization** on its own. **Translation in progress.** The full guide is available in Portuguese: [Integração com Calendário](https://docs.sendeasy.app/integrations/calendario.md). ## Endpoints | Method | Endpoint | Description | | -------- | ---------------------------------------------------- | ---------------------------- | | `GET` | `/api/v1/calendars` | List the company's calendars | | `GET` | `/api/v1/calendars/{id}/integrations/google/connect` | Get the authorization URL | | `GET` | `/api/v1/calendars/{id}/integrations/google` | Integration status | | `PUT` | `/api/v1/calendars/{id}/integrations/google` | Update options | | `DELETE` | `/api/v1/calendars/{id}/integrations/google` | Disconnect the integration | --- # Custom Integration The **Custom** integration generates a generic token that your application uses to call Sendeasy's public API and send messages (WhatsApp, Email or SMS), without needing a built-in connector. Full documentation of the endpoint, per-channel payloads and response codes lives at [Integrations (Server)](https://docs.sendeasy.app/en/server/integracoes.md). Configure the token in **Settings → Integrations → Custom Integration** inside your Sendeasy account. The token can be viewed at any time by the account administrator. --- # Eduzz — Events Sendeasy reacts to 5 Eduzz event types: invoice/pix issued, payment confirmed, refund, cancellation and expiration. **Documentation in progress.** This page will soon include the full event list with payloads, HMAC-SHA256 validation, mapping to WhatsApp and/or email send, and troubleshooting tips. --- # Eduzz Integration with **Eduzz** via webhooks to react to sales events (invoice paid, purchase confirmed, refund, etc.) by sending automated WhatsApp and email messages. **Documentation in progress.** This page will soon include the configuration walkthrough, supported events, HMAC validation and common troubleshooting. For now, configure the integration in **Settings → Integrations → Eduzz** inside your Sendeasy account. --- # Eduzz — Setup Step-by-step guide to connecting Eduzz and starting to receive sales events. **Documentation in progress.** This page will soon cover HMAC key creation, the webhook URL to register on Eduzz, event mapping and an end-to-end test. --- # GreatPages Sendeasy integration with the **GreatPages** platform, to receive leads from landing pages and start conversations automatically. **Documentation in progress.** This page will soon include the configuration walkthrough, expected payload format and common troubleshooting. For now, configure the integration in **Settings → Integrations → GreatPages** inside your Sendeasy account. --- # Respondi Sendeasy integration with the **Respondi** platform, to receive leads and trigger them as WhatsApp conversations. **Documentation in progress.** This page will soon include the configuration walkthrough, expected payload format and common troubleshooting. For now, configure the integration in **Settings → Integrations → Respondi** inside your Sendeasy account. --- # WABA — Webhook events Sendeasy forwards every event received from Meta to your configured webhook: incoming messages, delivery/read statuses, failures, template status changes and more. **Documentation in progress.** This page will soon list each event type (`messages`, `statuses`, `template_status_update`) with payload, HMAC signature and examples. --- # WABA Integration with the official **WhatsApp Business API** (Cloud API). Enables authenticated message sending, approved templates and high-volume operation within Meta's official policies. **Documentation in progress.** This page will soon include the configuration walkthrough, phone number linking, template creation and approval, and common troubleshooting. For now, configure the integration in **Settings → Integrations → WABA** inside your Sendeasy account. ## Request contact sharing [Request contact sharing](https://docs.sendeasy.app/en/integrations/waba/request-contact.md): Request a phone number through WABA/BSUID or ZuckZapGo/LID using the SendEasy public API. --- # Request contact sharing Send a message with the native share-contact button. The recipient chooses whether to share their phone number with the business. ## Routes and authentication Use the company API token created in **Settings → Integrations → API Tokens**. Restricted tokens need the `waba` scope, including on the unified route. Legacy tokens without scopes remain allowed. `CHANNEL_UUID` must belong to the token company. | POST | Authentication | | ----------------------------------------------- | ---------------------------------------------------------------------------------- | | `/api/v1/channels/CHANNEL_UUID/request-contact` | WABA or ZuckZapGo; company API token | | `/api/v1/waba/CHANNEL_UUID/request-contact` | WABA only; company API token | | `/api/messages/request-contact` | Channel associated with the connection token; Authorization: Bearer CHANNEL\_TOKEN | ## WABA example: request by phone number Replace `CHANNEL_UUID`, `YOUR_API_TOKEN` and the phone number with your integration values. Include the country code and use digits only. The message text must contain 1 to 1024 characters. ```bash curl --request POST 'https://backend.sendeasy.app/api/v1/channels/CHANNEL_UUID/request-contact' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --header 'Content-Type: application/json' \ --data '{"to":"5511999999999","body":"Share your contact so we can continue assisting you."}' ``` ## WABA: contact without a phone number If you receive only a BSUID, pass that opaque identifier in `to` unchanged. A username is a display attribute and does not replace the recipient identifier. SendEasy sends a native `request_contact_info` message to Meta. Availability and sending rules depend on the account and provider. ```json { "to": "BR.Example123", "body": "Share your contact so we can continue assisting you." } ``` ## ZuckZapGo: phone number or LID For a WhatsApp channel using ZuckZapGo/WuzAPI, use the same unified route and include `id`, required with 1 to 128 alphanumeric characters. The recipient can be a phone number, phone JID or full LID. BSUIDs, usernames, groups and device-suffixed JIDs are not accepted by this integration. ```json { "to": "123456789012345@lid", "body": "Share your contact so we can continue assisting you.", "id": "ContactRequest202610060001" } ``` ## Response and delivery confirmation HTTP 200 returns the provider response. For WABA, `messages[0].id` identifies the accepted message. Acceptance does not confirm delivery: follow Meta `statuses` events or query the status route below. This endpoint does not itself create an outgoing message or a ticket in the WABA inbox. ```json { "messaging_product": "whatsapp", "contacts": [{ "input": "5511999999999", "wa_id": "5511999999999" }], "messages": [{ "id": "wamid.EXAMPLE" }] } ``` ```bash curl 'https://backend.sendeasy.app/api/v1/waba/CHANNEL_UUID/messages/MESSAGE_ID/status' \ --header 'Authorization: Bearer YOUR_API_TOKEN' ``` ## When the recipient shares their contact The shared contact follows the normal inbound message flow, and the original payload is preserved when forwarding webhooks. For the official API, the card has `origin: "contact_request"`; the phone is in `contacts[].phones[].wa_id`. For ZuckZapGo, the `Message` event has `contactMessage.isSelfContact: true`; the vCard contains the shared identity. Correlation uses the conversation within the company and channel. An ordinary third-party contact card must not replace the sender identity. ## Errors and retries The stable ID and idempotency described here apply to ZuckZapGo. Reuse the same `id` and payload when a retry is allowed. SendEasy does not automatically retry. Including `id` on WABA does not add sending idempotency. | Error | Meaning | | ----------------------------------- | ---------------------------------------------------------------------------------------- | | `CONTACT_REQUEST_BODY_INVALID` | Empty text or more than 1024 characters. | | `CONTACT_REQUEST_RECIPIENT_INVALID` | Recipient is invalid for the provider. | | `CONTACT_REQUEST_ID_REQUIRED` | ZuckZapGo requires an alphanumeric ID with 1 to 128 characters. | | `CHANNEL_NOT_FOUND` | Channel is missing or outside the token company. | | `CONTACT_REQUEST_DISPATCH_UNKNOWN` | Outcome is unknown after a timeout/transport failure. Do not automatically resend. | | `contact_request_dispatch_unknown` | The provider cannot determine whether it dispatched. Do not resend or generate a new ID. | | `contact_request_id_conflict` | The ID was reused with different data. | Other errors retain the provider HTTP status and details in `provider`. Confirm that the account supports this message type. A failure documented by the provider as occurring before dispatch may allow a retry with the same ID and payload. See also [Public API](https://docs.sendeasy.app/en/server/api-publica.md) and [WABA API](https://docs.sendeasy.app/en/server/waba.md). --- # WABA — Setup Step-by-step guide to linking a phone number to the WhatsApp Business API (Cloud API) on Sendeasy. **Documentation in progress.** This page will soon include Meta Business prerequisites, Meta for Developers app creation, phone number linking, webhook verification and a test send. --- # WABA — Templates Templates are message formats pre-approved by Meta, used to start conversations outside the 24-hour window. **Documentation in progress.** This page will soon cover creation via Sendeasy, Meta approval policy, variables and send examples using `POST /api/v1/waba/:whatsappId/send`. --- # Webhook The **Webhook** integration sends events from your Sendeasy project (incoming/outgoing messages, delivery status, ticket changes, etc.) to an HTTPS URL of yours. Webhooks can be configured at two scopes: * **General** — receives events from every channel in the company. * **Per specific channel** — receives events only from the chosen channel. Full documentation, with event payload format and examples, is in [Integrations (Server)](https://docs.sendeasy.app/en/server/integracoes.md) and [Webhooks](https://docs.sendeasy.app/en/server/webhooks.md). Configure webhooks in **Settings → Integrations → Webhook** inside your Sendeasy account. --- # Set Up Kanban Kanban helps your team manage stages, ownership and SLA. ## Recommended setup 1. Define stages by business process. 2. Set entry rules for each stage. 3. Configure assignment rules. 4. Track bottlenecks by stage aging. ## Good practices * Keep stage names short and clear. * Avoid duplicate stage semantics. * Review stage metrics weekly. --- # Lead Capture Lead capture supports source tracking, enrichment and routing. ## Core fields * Name * Phone * Email * Source / UTM metadata ## Routing suggestions 1. Route by campaign/source. 2. Assign by region or queue. 3. Attach tags for downstream automations. --- # Introduction Welcome to Sendeasy documentation. This English section is focused on onboarding, integrations, and technical references so teams can implement fast. ## Where to start 1. Create your project. 2. Connect channels. 3. Configure workflows (Kanban, bots, automations). 4. Integrate using Server API or Web integrations. ## Developer-first docs If your team is integrating with public APIs and webhooks, start with: * [/en/server](https://docs.sendeasy.app/en/server.md) * [/en/server/referencia-tecnica](https://docs.sendeasy.app/en/server/referencia-tecnica.md) * [/en/server/api-publica](https://docs.sendeasy.app/en/server/api-publica.md) * [/en/server/webhooks](https://docs.sendeasy.app/en/server/webhooks.md) Need help with a specific integration flow? Use the support chat inside the platform and share your request/response payloads for faster troubleshooting. --- # Create a project and workspace In Sendeasy, a **project is the company workspace**. It contains the team, channels, contacts, tickets, Kanban, bots, flows, calendars and integrations. There is no separate “create workspace” step. ## Step by step 1. Sign in to the [Sendeasy console](https://console.sendeasy.app/). 2. On **Projects**, click **New Project**. 3. Enter the company name and the responsible person's name, email and mobile number. 4. Click **Next**, choose a plan and click **Create project**. 5. Complete checkout when it is displayed. 6. Open the new project card to enter the workspace. ## Recommended setup order 1. Connect a channel in **Channels**. 2. Create support stages in **Kanban**. 3. Invite agents and set permissions. 4. Configure bots or flows and associate them with the right stages. 5. Run a controlled test conversation. Each project is isolated. Channels, data and permissions are not automatically shared between projects. Sendy can manage resources inside the current project. The main project and plan are created through Projects so the owner can confirm commercial details. --- # Configure and use Sendpay Sendpay creates charges and Sendeasy checkout links. Funds are processed by and paid directly to your connected gateway account. | Gateway | PIX | Card | | ------------ | --- | ---- | | Stripe | No | Yes | | Mercado Pago | Yes | Yes | | Asaas | Yes | Yes | The Sendeasy Sendpay checkout offers PIX and cards, not bank slips. ## Setup 1. Open **Sendpay > Settings** and select a gateway. 2. Copy credentials from the gateway's official dashboard. 3. Enter the requested public and secret keys. 4. Enable supported methods and save. 5. Register the Sendeasy webhook URL and webhook secret in the gateway. 6. Create a small test charge and verify its status update. With Sendy, put the provider key in the secure **Credentials > Sendpay** field, then request the configuration in chat. The temporary key never enters the conversation and the change always requires confirmation. Never paste secrets into chat. Sendy can report whether a key is configured, but cannot reveal its value. --- # Public API (Server) ## Base URL ```txt https://backend.sendeasy.app ``` ## Authentication models 1. Channel token (`/api/messages/send`, `/api/v1/email/*`) 2. API token (`/api/v1/waba/*`, `/api/v1/ai/*`) 3. Integration token (`/api/integration/generic`) ## Main endpoints | Method | Endpoint | Auth | | ------ | -------------------------------- | ---------------------------------------------------------------------------------------------- | | `POST` | `/api/messages/send` | Channel token | | `POST` | `/api/integration/generic` | Integration token | | `POST` | `/api/v1/email/send` | Email channel token — see [/en/server/email](https://docs.sendeasy.app/en/server/email.md) | | `GET` | `/api/v1/email/health` | Email channel token | | `POST` | `/api/v1/ai/completion` | API token — see [/en/server/bots](https://docs.sendeasy.app/en/server/bots.md) | | `POST` | `/api/v1/ai/extract` | API token | | `POST` | `/api/v1/ai/search` | API token | | `GET` | `/api/v1/ai/health` | API token | | `POST` | `/api/v1/diagnostic-submissions` | API token — see [/en/server/diagnosticos](https://docs.sendeasy.app/en/server/diagnosticos.md) | ## Success and error contracts Success example: ```json { "message": "Message queued successfully" } ``` Error example: ```json { "error": "ERR_NO_PERMISSION" } ``` ## Translation strategy Keep `error` as canonical key and resolve display messages by locale in your client. --- # Bots API Use Sendeasy's AI **from your own application** — an assistant inside your product, a routine in your backend, a screen that summarizes documents — without going through WhatsApp. The bot carries the instructions: you create and edit it in the panel, and pass its `botId` here. Changing how the AI behaves means editing the bot, not changing your code. ## Prerequisites 1. **Project API Token** — in **Integrations → WABA** → *API Tokens* section, where you create a named token and can revoke it. It is the same token used by the WABA API and the Email API: one per project, not one per product. 2. **A bot created** in the panel, with its instructions and knowledge sources — see [/en/bot](https://docs.sendeasy.app/en/bot.md). Keep the `botId`. 3. **Bot credits** available in the project plan. Use [`GET /api/v1/ai/health`](https://docs.sendeasy.app/en/server/bots.md#integration-diagnostics) to check the integration without spending credits. ## Authentication Required header: ```http Authorization: Bearer ``` The token identifies the **project**, and it is what authorizes the `botId`: a bot from another project is rejected, even if you know its id. It is the **same API Token** used by the [WABA API](https://docs.sendeasy.app/en/server/waba.md). Revoking it in **Integrations → WABA** breaks every integration that relies on it, not just this one. ## Endpoints | Method | Endpoint | Purpose | Credit | | ------ | ----------------------- | ----------------------------------- | ---------------- | | `POST` | `/api/v1/ai/completion` | Generate text with the bot's prompt | 1 per generation | | `POST` | `/api/v1/ai/extract` | Extract content from a PDF | 1 per file | | `POST` | `/api/v1/ai/search` | Search the bot's knowledge base | free | | `GET` | `/api/v1/ai/health` | Diagnostics: is the integration up? | free | Limit: **60 requests per minute** per API Token (`429 ERR_PUBLIC_AI_RATE_LIMIT`). Counting is per token, not per IP — several servers sharing one token share the quota, and the same server with different tokens gets separate quotas. ## Text generation ```txt POST /api/v1/ai/completion ``` ### Payload | Field | Type | Required | Notes | | ---------------- | -------- | -------------- | ---------------------------------------------------- | | `prompt` | `string` | Yes | the request itself, up to 120,000 characters | | `botId` | `string` | One of the two | uses that bot's instructions and knowledge | | `instructions` | `string` | One of the two | standalone system prompt, up to 60,000 characters | | `responseFormat` | `string` | No | `text` (default) or `json` | | `timeoutMs` | `number` | No | between 1,000 and 90,000; service default if omitted | Pass `botId` **or** `instructions`. With neither, the route answers `400` instead of spending a credit to return out-of-context text. ### Example ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/completion' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --data '{ "botId": "your-bot-id", "prompt": "Summarize the return policy in two paragraphs." }' ``` Success response: ```json { "success": true, "result": "The return policy allows returns within 30 days..." } ``` With `"responseFormat": "json"`, the answer comes parsed in `dados` instead of `result` — useful when the prompt asks for a structure and you would rather not parse text: ```json { "success": true, "dados": { "prazo_dias": 30, "exige_nota": true } } ``` Every request holds a connection open until the AI answers. Prefer a short `timeoutMs` on screens where someone is waiting, and leave the 90s ceiling for background routines. ## PDF extraction ```txt POST /api/v1/ai/extract ``` `multipart/form-data` upload with the file in the `file` field. PDF only, up to **25 MB**. | Field | Type | Required | Notes | | ------ | -------- | -------- | ---------------------------------------------------------------------------------------- | | `file` | `file` | Yes | the PDF, `application/pdf` | | `mode` | `string` | No | `text` (default) returns the text in `result`; other modes return the payload in `dados` | ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/extract' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --form 'file=@"/path/contract.pdf"' \ --form 'mode="text"' ``` ```json { "success": true, "result": "SERVICE AGREEMENT..." } ``` Reading falls back to OCR for scanned PDFs, so large files take a while. If the AI cannot recognize the content, the answer is `502` with the reason — resending the same file will not help. ## Knowledge base search ```txt POST /api/v1/ai/search ``` Retrieves excerpts from the bot's sources without generating text. Use it to ground an answer of your own, or to show the user where the information came from. | Field | Type | Required | Notes | | --------- | -------- | -------- | --------------------------------------------------- | | `botId` | `string` | Yes | which bot to search | | `query` | `string` | Yes | what to look for, up to 2,000 characters | | `topK` | `number` | No | how many excerpts to return, from 1 to 8 | | `filters` | `object` | No | narrows the search to certain documents (see below) | `filters` takes two fields, both lists of file-name patterns (`%` matches any stretch): | Field | Effect | | ----------------- | -------------------------------------------------------------------------- | | `incluirArquivos` | searches **only** the matching documents; omitted, it scans the whole base | | `excluirArquivos` | drops matching documents, even if they were included | ```json { "botId": "your-bot-id", "query": "warranty period", "filters": { "incluirArquivos": ["manual-%", "returns-policy.pdf"], "excluirArquivos": ["draft-%"] } } ``` Useful when the base is large and you already know where the answer lives: without a filter, one long document can take up the result slots and drown out what matters. ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/search' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --data '{ "botId": "your-bot-id", "query": "warranty period", "topK": 3 }' ``` ```json { "success": true, "trechos": [ { "texto": "The legal warranty is 90 days for durable goods...", "procedencia": "customer-manual.pdf", "score": 0.82 } ] } ``` No credit is charged: this is content retrieval, not generation. If the search fails, the answer carries `trechos: []` instead of an error — without context your answer loses grounding, it does not cease to exist. ## Integration diagnostics ```txt GET /api/v1/ai/health ``` Free, and it never calls the model. It answers the two questions behind almost every integration failure: is the AI service up, and is there still balance? ```bash curl --location 'https://backend.sendeasy.app/api/v1/ai/health' \ --header 'Authorization: Bearer YOUR_API_TOKEN' ``` ```json { "success": true, "companyId": 42, "botrag": { "ok": true, "latenciaMs": 210 }, "creditos": { "disponivel": 4820, "mes": "9/2026" }, "checkedAt": "2026-09-01T12:30:00.000Z" } ``` * `botrag.ok: false` — the AI service is unavailable; a `detalhe` field carries the cause. Nothing to do on your side other than retry later. * `creditos.disponivel: 0` — the project ran out of credits and the generation routes will answer `402`. Renew the plan or wait for next month's allowance. ## Which bots a token can reach `botId` is resolved **inside your API Token's project**. A token from one project cannot use another project's bot, even knowing its id: the route answers `403` without generating anything or touching your balance. A bot that doesn't exist answers `404`. This applies to `completion` and `search` — the two routes that resolve a bot. `extract` doesn't use `botId`. A bot's instructions are usually the result of a lot of fine-tuning, so we treat them as project content: no route returns the prompt text, and no token reaches another project's bot. ## Credits and refunds * Every generation (`completion`, `extract`) deducts **1 bot credit** from the project. * With no credits, the route answers `402 ERR_NO_BOT_CREDITS` **before** any charge. * If generation fails **after** the charge, the credit is **refunded automatically** and the route answers `502`. * `search` and `health` cost nothing. The balance is the **same** as the support bot's on WhatsApp — there is no separate quota for the API. Heavy usage here leaves the WhatsApp bot without credits, and the other way around. ## Error codes | HTTP | `error` | When it happens | | ---- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | 400 | validation message | invalid payload, missing `prompt`, neither `botId` nor `instructions`, file that is not a PDF | | 400 | `INVALID_JSON` | body is not valid JSON | | 400 | `FILE_TOO_LARGE` | PDF above 25 MB | | 401 | `ERR_API_TOKEN_NOT_PROVIDED` / `ERR_API_TOKEN_INVALID_FORMAT` / `ERR_API_TOKEN_INVALID` / `ERR_API_TOKEN_AUTHENTICATION_FAILED` | missing or malformed header, invalid or revoked token | | 402 | `ERR_NO_BOT_CREDITS` | project has no bot credits (nothing was charged) | | 403 | authorization message | `botId` of a bot that does not belong to the token's project | | 404 | bot message | `botId` does not exist | | 413 | `PAYLOAD_TOO_LARGE` | JSON body above 10 MB | | 429 | `ERR_PUBLIC_AI_RATE_LIMIT` | more than 60 requests per minute | | 502 | plain-text message | generation failed (credit refunded); when the AI knows why, the reason comes in the message | | 504 | `GATEWAY_TIMEOUT` | generation exceeded the time limit | The `502` is the only one that arrives with a free-form message instead of a code — handle it **by status**, not by the text, which may change. The others follow the platform's code contract, listed in [/en/server/traducoes](https://docs.sendeasy.app/en/server/traducoes.md). ## Bots API × bot on WhatsApp The same bot, two paths — what changes is who drives the conversation: | | Bots API (`/api/v1/ai/*`) | Bot in support | | ------------ | ----------------------------------------- | ---------------------------------------- | | Who calls | your application | the ticket, when it enters the queue | | Conversation | each call stands alone | Sendeasy keeps the history in the ticket | | Typical use | assistant and automations in your product | support on WhatsApp | **Security:** treat the token like a password. Never embed it in browser JavaScript, public repositories or logs — keep the calls on your server, with the token in an environment variable or a secret manager. --- # Diagnostic Submissions If you run a diagnostic form (quiz, assessment, self-test) that delivers a result to the lead, this API logs **every submission attempt** in Sendeasy: who filled it in, the result, and whether the WhatsApp delivery and the ActiveCampaign contact creation succeeded. The history shows up under **Leads → Diagnostic Submissions**, with search and CSV export. This API only records the history. It does not send the message or create the contact — that is still done by your form, by the [WABA API](https://docs.sendeasy.app/en/server/waba.md), or by the integration you already use. ## Before you start 1. **Register the project.** Under **Leads → Diagnostic Submissions**, open **Allowed projects** and register an identifier for your diagnostic (e.g. `mydiagnostic`). While the list is empty, no submission is recorded. 2. **Generate an API Token** for the project (Settings → API). The identifier you send must **exactly match** one of the registered ones. This validation is intentional: it prevents a typo from silently creating a new project. ## Log a submission | Method | Endpoint | Auth | | ------ | -------------------------------- | --------- | | `POST` | `/api/v1/diagnostic-submissions` | API Token | ### Request body | Field | Type | Required | Description | | ---------------------- | ------ | -------- | ---------------------------------------------------------------------------- | | `project` | string | **Yes** | Diagnostic identifier, matching the one registered under "Allowed projects". | | `name` | string | No | Name of the person who filled it in. | | `email` | string | No | Email of the person who filled it in. | | `phone` | string | No | Phone with country and area code (e.g. `5511999999999`). | | `result` | object | No | Answers, scores and dimensions of the diagnostic. Free-form. | | `whatsappStatus` | string | No | `success`, `failed` or `skipped`. | | `whatsappError` | string | No | Error message when the delivery fails. | | `activeCampaignStatus` | string | No | `success`, `failed` or `skipped`. | | `activeCampaignError` | string | No | Error message when the integration fails. | | `source` | string | No | Lead source (campaign, page, ad). | Send the status fields **after** attempting delivery, with the real outcome of each step. That is what lets you find failed deliveries later. ### Example ```bash curl --location 'https://backend.sendeasy.app/api/v1/diagnostic-submissions' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --data '{ "project": "mydiagnostic", "name": "Maria Souza", "email": "maria@example.com", "phone": "5511999999999", "result": { "average": 7.5, "categories": { "process": 8, "people": 7 } }, "whatsappStatus": "success", "activeCampaignStatus": "failed", "activeCampaignError": "API timeout", "source": "instagram-campaign" }' ``` ### Response ```json { "id": 42 } ``` ## Errors | HTTP | `error` | What to do | | ----- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `400` | `PROJECT_REQUIRED` | Send the `project` field. | | `400` | `NO_PROJECTS_CONFIGURED` | No project registered yet: open "Allowed projects" on the screen and register the identifier. | | `400` | `PROJECT_NOT_ALLOWED` | The identifier is not on the list. The response includes `allowedProjects` with the accepted values — compare them with what your form sends. | | `401` | `ERR_API_TOKEN_NOT_PROVIDED` | Missing the `Authorization: Bearer ` header. | | `401` | `ERR_API_TOKEN_INVALID` | Invalid or disabled token. | Both project errors include the accepted list: ```json { "error": "PROJECT_NOT_ALLOWED", "allowedProjects": ["mydiagnostic"] } ``` ## Best practices * **Log failures too.** A submission with `whatsappStatus: "failed"` is exactly what you want to see later. * **Don't block the user.** Call this API in the background: if logging fails, the lead should still see their result. * **Keep the token on the server.** The API Token grants access to the project — never expose it in the browser. --- # Email API Send transactional emails (password reset, notices, confirmations) **through Sendeasy** from your own application, authenticated by the **token of the email itself** — every email configured in the project has its own. Messages go out **from the email that owns the token** and consume **1 email credit** of the project. You never pass the sender: it is the token's email. Have more than one email? Use the token of the one you want to send from. ## Prerequisites 1. **Email token** — in **Channels → Email** (or on the email card in **Channels**) → *Settings* → **Sending API** section. Each email has its own token; you can also **generate a new one** there (the previous one stops working immediately). 2. The email with status **Verified** (same place). Without it the route answers `503 ERR_EMAIL_SENDER_NOT_CONFIGURED` and nothing is sent, even with credits. 3. **Email credits** available in the project plan. Use [`GET /api/v1/email/health`](https://docs.sendeasy.app/en/server/email.md#integration-diagnostics) to check items 1 and 2 without spending credits. ## Authentication Required header, with the sender email's token: ```http Authorization: Bearer ``` The token identifies the **sender email** and, through it, the project — there is no `whatsappId`/`emailId` in the URL. **Compatibility:** the project **API Token** (the WABA API one) is also accepted in this header. In that case the sender is the project's **first verified email** — handy for integrations built before per-email tokens, but to choose the sender use the email token. ## Endpoints | Method | Endpoint | Purpose | Credit | | ------ | ---------------------- | ------------------------------------------- | ---------- | | `POST` | `/api/v1/email/send` | Send a transactional email | 1 per send | | `GET` | `/api/v1/email/health` | Diagnostics: is the token's email verified? | none | Rate limit: **20 requests per minute** per origin (`429 ERR_PUBLIC_EMAIL_RATE_LIMIT`). ## Sending an email ```txt POST /api/v1/email/send ``` ### Payload | Field | Type | Required | Notes | | ---------- | ---------------------- | -------------- | ---------------------------------------------------------------------------------------------- | | `to` | `string` or `string[]` | Yes | up to **10 recipients** per send | | `subject` | `string` | Yes | up to 200 characters | | `html` | `string` | One of the two | HTML body, up to 200,000 characters | | `text` | `string` | One of the two | plain-text body, up to 20,000 characters | | `fromName` | `string` | No | sender display name (up to 80 characters). The address is always the email that owns the token | The sender address (`fromEmail`) is **not accepted** in the payload: the route always uses the email that owns the token. This prevents a token from sending on behalf of an email that is not yours. ### Example ```bash curl --location 'https://backend.sendeasy.app/api/v1/email/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer EMAIL_TOKEN' \ --data '{ "to": "customer@example.com", "subject": "Password reset", "fromName": "My Platform", "html": "

Hi! Click the link below to create a new password.

" }' ``` Success response: ```json { "success": true, "id": "9fbbb3a9-4c1e-4b1a-9d1e-0c2b7e3f5a61" } ``` `id` is the provider's identifier for the send. The response confirms the email was **accepted for delivery** — final delivery depends on the recipient's mail server. ## Integration diagnostics ```txt GET /api/v1/email/health ``` Does not consume credits. Tells whether the token's email is already verified — the most common reason a send fails even with credits available. ```bash curl --location 'https://backend.sendeasy.app/api/v1/email/health' \ --header 'Authorization: Bearer EMAIL_TOKEN' ``` ```json { "ok": true, "senderConfigured": true, "sender": "no-reply@yourcompany.com", "domain": "yourcompany.com", "channelId": 12 } ``` With `senderConfigured: false`, finish verifying the email in **Channels → Email** before sending. (With the project API Token the response has no `channelId` and tells whether the project has *any* verified email.) ## Credits and refunds * Every accepted send debits **1 email credit** from the project. * With no credits, the route answers `402 ERR_NO_EMAIL_CREDITS` **before** any debit. * If the send fails **after** the debit (sender not configured, provider unavailable), the credit is **refunded automatically** and the route answers `502`/`503`. ## Error codes | HTTP | `error` | When it happens | | ---- | ------------------------------------------------------ | --------------------------------------------------------------------------------------- | | 400 | `ERR_EMAIL_INVALID_BODY` | invalid payload (missing `to`/`subject`, neither `html` nor `text`, no valid recipient) | | 400 | `ERR_EMAIL_TOO_MANY_RECIPIENTS` | more than 10 recipients in one send | | 401 | `ERR_API_TOKEN_NOT_PROVIDED` / `ERR_API_TOKEN_INVALID` | header missing or token invalid/inactive | | 402 | `ERR_NO_EMAIL_CREDITS` | project has no email credits | | 429 | `ERR_PUBLIC_EMAIL_RATE_LIMIT` | more than 20 requests per minute | | 502 | `ERR_EMAIL_SEND_FAILED` | provider failure while sending (credit refunded) | | 503 | `ERR_EMAIL_SENDER_NOT_CONFIGURED` | the token's email is not verified yet (with an email token, before any debit) | ## Email API vs. Custom integration Sendeasy offers two ways to send email by API — pick by the kind of token you have: | | Email API (`/api/v1/email/send`) | Custom (`/api/integration/generic`) | | ----------- | ------------------------------------------------ | ------------------------------------------------------ | | Token | **email token** (Channels → Email → Sending API) | integration token bound to **one** email channel | | Sender | the email that owns the token | the token's channel | | Typical use | transactional emails from your own application | per-channel integrations with `bcc`, `inReplyTo`, etc. | Custom integration details at [/en/server/integracoes](https://docs.sendeasy.app/en/server/integracoes.md). **Security:** treat the token like a password. Never embed it in browser JavaScript, public repositories or logs — keep it in environment variables or a secret manager on your server. --- # Integrations (Server) Sendeasy offers two integration products: * **Webhook** — Sendeasy pushes events from your project to a URL you control. * **Custom (generic)** — your application calls Sendeasy to send messages. Webhooks support **two scopes**: * **General**: receives events from **every** channel of the company. * **Channel-specific**: receives events **only** from the chosen channel (one WhatsApp instance, one Email mailbox, one SMS number). The two scopes coexist: you can have 1 general webhook + N per-channel webhooks active at the same time. Each event fires the UNION of applicable webhooks (general + specific). **Custom** tokens always require a specific channel. ## UI configuration Go to **Settings → Integrations**. There you'll see a list of integrations. For each one: 1. Click **New integration**. 2. For webhook, choose the **scope**: *General (all channels)* or *Specific channel*. 3. If "Specific channel", pick the **type** (`whatsapp`/`email`/`sms`) and instance. 4. For **webhook**: provide the URL and, optionally, a bearer token sent in the `Authorization` header. 5. For **custom**: the token is generated by the system and shown **only once**. Copy and save it immediately. ## Sending messages — `POST /api/integration/generic` This endpoint now has **different payloads per channel type**. The destination channel is **derived from the token** — your application does NOT send `channelType` in the body. A token created for Email can only send Email; an SMS token can only send SMS. Required header: ``` Authorization: Bearer ``` ### WhatsApp ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "name": "WhatsApp Lead", "number": "5511999999999", "message": "I want to talk to sales", "email": "lead@company.com", "url": "https://landing.com/?utm_source=meta", "files": ["https://landing.com/deck.pdf"], "sector": "12" }' ``` ```json { "success": true, "channel": "whatsapp", "messageId": null, "contactId": null } ``` | Field | Type | Required | | --------- | ---------------------------- | -------- | | `number` | `string` (E.164 without `+`) | yes | | `message` | `string` | yes | | `name` | `string` | no | | `email` | `string` | no | | `url` | `string` (UTM) | no | | `files` | `string[]` (URLs) | no | | `sector` | `string` (queue id) | no | ### Email ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "to": ["customer@company.com"], "subject": "Order confirmation", "html": "

Thanks for your purchase!

", "bcc": ["archive@company.com"] }' ``` ```json { "success": true, "channel": "email", "messageId": "msg_abc123", "contactId": null } ``` | Field | Type | Required | | ---------------- | ------------------------------- | -------- | | `to` | `string` or `string[]` (emails) | yes | | `subject` | `string` | yes | | `body` or `html` | `string` | one of | | `bcc` | `string` or `string[]` | no | | `text` | `string` | no | | `fromName` | `string` | no | | `inReplyTo` | `string` | no | | `references` | `string` or `string[]` | no | The sender address (`fromEmail`) is the channel bound to the token. ### SMS ```bash curl -X POST https://backend.sendeasy.app/api/integration/generic \ -H "Authorization: Bearer 12G3N3R1$..." \ -H "Content-Type: application/json" \ -d '{ "to": "+5511999999999", "body": "Your code: 123456" }' ``` ```json { "success": true, "channel": "sms", "messageId": "SMxxxxxxxx", "contactId": null } ``` | Field | Type | Required | | ----------- | ------------------------- | -------- | | `to` | `string` (E.164 with `+`) | yes | | `body` | `string` (≤ 1600 chars) | yes | | `mediaUrls` | `string[]` (public URLs) | no | | `metadata` | `object` | no | ## Response codes | HTTP | `error` | Meaning | | ---- | ------------------- | ------------------------------------ | | 200 | — | Success | | 400 | `VALIDATION_ERROR` | Invalid payload — see `details[]` | | 401 | `ERR_NO_PERMISSION` | Missing/invalid token | | 402 | `PROVIDER_ERROR` | No credits on this channel | | 404 | `CHANNEL_NOT_FOUND` | Channel deleted after token created | | 502 | `PROVIDER_ERROR` | Provider failure (Twilio, Resend, …) | | 410 | `GONE` | Deprecated legacy endpoint | ## Token rotation `custom` tokens can be rotated at any time under **Settings → Integrations**. **Rotation invalidates the previous token immediately.** We recommend periodic rotation (90 days) and immediate rotation on suspected leaks. ## Security **Never** embed tokens in browser JavaScript, public repos, screenshots, or logs. Treat the token like a password. Store it in environment variables, a vault, or a secret manager. If exposed, rotate immediately. ## Where to manage All token and webhook management lives on the platform, under **Settings → Integrations**. Previously created tokens continue to work — just review and adjust the configuration on the new screen. --- # Server Integration This section is the technical entry point for backend integrations with Sendeasy. ## What is covered * **Technical Reference**: practical request/response contracts. * **Public API**: authentication, payloads and endpoint catalog. * **Integrations**: public Generic flow. * **WABA API**: template and media endpoints. * **Email API**: transactional email sending via `/api/v1/email/*`, billed in project credits. * **Bots API**: text generation, PDF extraction and search via `/api/v1/ai/*`, billed in project bot credits. * **Webhooks**: events effectively forwarded to your project webhook. * **Error Translations**: multilingual error catalog strategy. ## Recommended reading order 1. [/en/server/referencia-tecnica](https://docs.sendeasy.app/en/server/referencia-tecnica.md) 2. [/en/server/api-publica](https://docs.sendeasy.app/en/server/api-publica.md) 3. [/en/server/integracoes](https://docs.sendeasy.app/en/server/integracoes.md) 4. [/en/server/waba](https://docs.sendeasy.app/en/server/waba.md) 5. [/en/server/email](https://docs.sendeasy.app/en/server/email.md) 6. [/en/server/bots](https://docs.sendeasy.app/en/server/bots.md) 7. [/en/server/webhooks](https://docs.sendeasy.app/en/server/webhooks.md) 8. [/en/server/traducoes](https://docs.sendeasy.app/en/server/traducoes.md) --- # Technical Reference Developer-focused runbook for real integrations: endpoint, payload, response and operational behavior. ## Integration TL;DR * Base URL: `https://backend.sendeasy.app` * Auth: `Authorization: Bearer ` * Formats: `application/json` and `multipart/form-data` * Many flows are queued asynchronously * Treat `error` as stable machine contract ## Main send endpoint `POST /api/messages/send` ```bash curl --location 'https://backend.sendeasy.app/api/messages/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer CHANNEL_TOKEN' \ --data '{ "number": "5511999999999", "body": "Hello from integration" }' ``` Typical response: ```json { "message": "Message queued successfully" } ``` ## WABA quick reference Prefix: ```txt /api/v1/waba/:whatsappId ``` Examples: * `GET /templates` * `POST /send` * `POST /send-text` * `POST /send-image` * `POST /send-document` * `POST /marketing-lite/send` ## Email API quick reference Transactional email from the email that owns the token (each project email has its own, in Channels → Email), 1 email credit per send: * `POST /api/v1/email/send` — body `{ to, subject, html | text, fromName? }` * `GET /api/v1/email/health` — is the token's email verified? (no credit) Full reference at [/en/server/email](https://docs.sendeasy.app/en/server/email.md). ## Bots API quick reference Text generation, PDF extraction and knowledge base search, 1 bot credit per generation (the same balance as the WhatsApp support bot). The prompt lives in the bot (`botId`); no credit without balance answers `402 ERR_NO_BOT_CREDITS`: * `POST /api/v1/ai/completion` — body `{ prompt, botId | instructions, responseFormat? }` * `POST /api/v1/ai/extract` — multipart, `file` field (PDF up to 25 MB) * `POST /api/v1/ai/search` — body `{ botId, query, topK? }`; retrieval only (no credit) * `GET /api/v1/ai/health` — is the AI service up and is there balance? (no credit) Full reference at [/en/server/bots](https://docs.sendeasy.app/en/server/bots.md). ## Webhook quick reference Payload shape sent to your webhook URL: ```txt { event, channel, data, channelId } ``` Example: ```json { "event": "messages.upsert", "channel": "whatsapp", "data": {}, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ## Error strategy ```js function normalizeApiError(status, body) { return { status, code: body?.error || 'UNKNOWN_ERROR', message: body?.message || null, } } ``` For complete endpoint-level details, continue with the pages in this server section (Public API, Integrations, WABA, Webhooks and Error Translations). --- # Error Translations (PT / EN / ES) This page centralizes translations for integration error codes used by Sendeasy. ## Frontend parity Use the same frontend model (`backendErrors`) to keep consistency between: * messages shown inside the web dashboard * messages displayed in external integrations (portals, ERPs, custom apps) Recommended strategy: 1. Backend returns `error` (stable code). 2. Your client translates via locale catalog. 3. Fallback to the error code when translation is missing. ## Integration usage 1. Treat `error` as a stable machine key. 2. Display `message` when available. 3. Optionally fallback to your local translation catalog. ## Common codes | Code | Português | English | Español | | ------------------------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | `ERR_NO_PERMISSION` | Você não tem permissão para acessar este recurso. | You do not have permission to access this resource. | No tienes permiso para acceder a este recurso. | | `ERR_SESSION_EXPIRED` | Sessão expirada. Por favor, faça login. | Session expired. Please log in. | Sesión expirada. Por favor, inicia sesión. | | `ERR_API_TOKEN_NOT_PROVIDED` | API token não informado. | API token was not provided. | No se proporcionó API token. | | `ERR_API_TOKEN_INVALID` | API token inválido. | Invalid API token. | API token inválido. | | `ERR_API_TOKEN_INVALID_FORMAT` | Formato de API token inválido. | Invalid API token format. | Formato de API token inválido. | | `ERR_API_TOKEN_AUTHENTICATION_FAILED` | Falha na autenticação do API token. | API token authentication failed. | Fallo en la autenticación del API token. | | `ERR_SENDING_TEMPLATE` | Erro ao enviar template. | Error sending template. | Error al enviar plantilla. | | `ERR_SENDING_MESSAGE` | Erro ao enviar mensagem. | Error sending message. | Error al enviar mensaje. | | `ERR_SENDING_IMAGE` | Erro ao enviar imagem. | Error sending image. | Error al enviar imagen. | | `ERR_SENDING_VIDEO` | Erro ao enviar vídeo. | Error sending video. | Error al enviar video. | | `ERR_SENDING_AUDIO` | Erro ao enviar áudio. | Error sending audio. | Error al enviar audio. | | `ERR_SENDING_DOCUMENT` | Erro ao enviar documento. | Error sending document. | Error al enviar documento. | | `ERR_INVALID_WABA_RECIPIENT` | Destinatário WABA inválido. | Invalid WABA recipient. | Destinatario WABA inválido. | | `ERR_EMAIL_INVALID_BODY` | Payload de e-mail inválido. | Invalid email payload. | Payload de email inválido. | | `ERR_EMAIL_TOO_MANY_RECIPIENTS` | Muitos destinatários em um único envio (máximo 10). | Too many recipients in a single send (max 10). | Demasiados destinatarios en un solo envío (máximo 10). | | `ERR_NO_EMAIL_CREDITS` | Sem créditos de e-mail. | No email credits. | Sin créditos de email. | | `ERR_NO_BOT_CREDITS` | Sem créditos de bot. | No bot credits. | Sin créditos de bot. | | `ERR_PUBLIC_EMAIL_RATE_LIMIT` | Limite de requisições de e-mail excedido. | Email request limit exceeded. | Límite de solicitudes de email excedido. | | `ERR_PUBLIC_AI_RATE_LIMIT` | Limite de requisições de IA excedido. | AI request limit exceeded. | Límite de solicitudes de IA excedido. | | `ERR_EMAIL_SEND_FAILED` | Falha ao enviar e-mail. | Failed to send email. | Error al enviar email. | | `ERR_EMAIL_SENDER_NOT_CONFIGURED` | Projeto sem e-mail verificado para envio. | Project has no verified sender email. | Proyecto sin email verificado para envío. | | `ERR_WAPP_NOT_CONNECTED` | WhatsApp não está conectado. | WhatsApp is not connected. | WhatsApp no está conectado. | | `ERR_WIDGET_TOKEN_INVALID` | Token do widget inválido. | Invalid widget token. | Token del widget inválido. | | `ERR_WIDGET_TOKEN_NOT_PROVIDED` | Token do widget não informado. | Widget token was not provided. | No se proporcionó token del widget. | | `ERR_TRY_AGAIN_LATER` | Por favor, tente novamente mais tarde. | Please try again later. | Por favor, intenta de nuevo más tarde. | | `FILE_TOO_LARGE` | Arquivo muito grande. O tamanho máximo é de 10MB. | File too large. The maximum size is 10MB. | Archivo demasiado grande. El tamaño máximo es de 10MB. | ## Client-side example ```js const fallbackTranslations = { ERR_NO_PERMISSION: { pt: 'Você não tem permissão para acessar este recurso.', en: 'You do not have permission to access this resource.', es: 'No tienes permiso para acceder a este recurso.', }, } function resolveErrorMessage(errorCode, locale, backendMessage) { return backendMessage || fallbackTranslations?.[errorCode]?.[locale] || errorCode } ``` If your product is multilingual, version this catalog with your client to keep UX predictable across releases. --- # WABA API Reference for WhatsApp Business API integration through Sendeasy public routes. ## Authentication Required header: ```http Authorization: Bearer ``` ## Route prefix ```txt /api/v1/waba/:whatsappId ``` ## Templates | Method | Endpoint | Purpose | | -------- | -------------------------- | --------------- | | `GET` | `/templates` | List templates | | `POST` | `/templates` | Create template | | `PUT` | `/templates/:templateId` | Update template | | `DELETE` | `/templates/:templateName` | Delete template | ## Messages | Method | Endpoint | Purpose | | ------ | ----------------------------- | -------------------------------------------------------- | | `POST` | `/send` | Send template (or plain text with specific payload body) | | `POST` | `/send-text` | Send text | | `POST` | `/send-image` | Send image | | `POST` | `/send-video` | Send video | | `POST` | `/send-audio` | Send audio | | `POST` | `/send-document` | Send document | | `POST` | `/marketing-lite/send` | Send marketing message (Marketing Messages Lite API) | | `GET` | `/messages/:messageId/status` | Fetch status history for a message | ## New API: marketing message Public endpoint: ```txt POST /api/v1/waba/:whatsappId/marketing-lite/send ``` Meta endpoint used by the backend: ```txt POST /{phone-number-id}/marketing_messages ``` Prerequisites: 1. WABA account with Marketing Messages Lite onboarding completed. 2. Marketing Messages Lite terms accepted in Meta account. 3. Template in `MARKETING` category with `APPROVED` status. ### Payload | Field | Type | Required | Notes | | ------------------------ | --------- | -------- | ----------------------------------------------- | | `to` | `string` | Yes | international number format (digits only) | | `templateName` | `string` | Yes | approved template name in Meta | | `language` | `string` | No | default: `pt_BR` | | `components` | `array` | No | template components/parameters | | `productPolicy` | `string` | No | `CLOUD_API_FALLBACK` (default) or `STRICT` | | `messageActivitySharing` | `boolean` | No | default: `true` | | `bizOpaqueCallbackData` | `string` | No | echoed back in webhook for campaign correlation | ### Example: send marketing message ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/YOUR_WHATSAPP_ID/marketing-lite/send' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --data '{ "to": "5511999999999", "templateName": "promo_blackfriday", "language": "pt_BR", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "John" } ] } ], "productPolicy": "CLOUD_API_FALLBACK", "messageActivitySharing": true, "bizOpaqueCallbackData": "campaign_blackfriday_2026" }' ``` Success response (example): ```json { "messages": [ { "id": "wamid.HBg..." } ] } ``` Limit/opt-out error (example): ```json { "error": "MARKETING_LIMIT_REACHED", "message": "User has opted out of marketing messages or reached the limit", "details": "..." } ``` ## Example: send text ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/YOUR_WHATSAPP_ID/send-text' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer YOUR_API_TOKEN' \ --data '{ "to": "5511999999999", "text": "Hello! Message sent through WABA API." }' ``` ## Example: send template ```json { "to": "5511999999999", "templateName": "welcome", "language": "en_US", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "John" } ] } ] } ``` ### Template with media header (IMAGE / VIDEO / DOCUMENT) When the approved template has a media header, you must send the `header` component with `parameters`. Use lowercase `type` (`header`/`body`), and the image must be served from a **public URL of your own** (S3, CDN, etc.) — do not use the `header_handle` returned in the template definition, it expires. ```json { "to": "5511999999999", "templateName": "promo_launch", "language": "en_US", "components": [ { "type": "header", "parameters": [ { "type": "image", "image": { "link": "https://your-cdn.com/banner.jpg" } } ] } ] } ``` Alternatively, replace `link` with `id` using the `media_id` returned by upload at `/api/v1/waba/:whatsappId/media`. ## Message status lookup Meta's Cloud API **does not expose a public endpoint to query message status on demand** — status is delivered exclusively via webhook (`sent` / `delivered` / `read` / `failed`). Sendeasy persists every received event and serves the full history at this route, in the same format as the webhook payload. ```txt GET /api/v1/waba/:whatsappId/messages/:messageId/status ``` ### Example ```bash curl --location 'https://backend.sendeasy.app/api/v1/waba/YOUR_WHATSAPP_ID/messages/wamid.HBg.../status' \ --header 'Authorization: Bearer YOUR_API_TOKEN' ``` ### Response ```json { "statuses": [ { "id": "wamid.HBgMNTUzMT...", "status": "sent", "timestamp": "1710000000", "recipient_id": "5511999999999" }, { "id": "wamid.HBgMNTUzMT...", "status": "failed", "timestamp": "1710000003", "recipient_id": "5511999999999", "errors": [ { "code": 131026, "title": "Message undeliverable" } ] } ] } ``` When no event has been recorded yet for the `messageId`, the response is `404` with `statuses: []` — just retry after a few seconds. Only messages sent through the WABA channel have their status persisted at this route. ## Common error codes | Code | Typical cause | | ------------------------------ | -------------------------------------------------- | | `WHATSAPP_NOT_FOUND` | `whatsappId` does not exist | | `WHATSAPP_CREDENTIALS_MISSING` | channel missing required WABA credentials | | `MISSING_PARAMETERS` | required payload fields are missing | | `MARKETING_LIMIT_REACHED` | recipient opted out or marketing limit was reached | | `WHATSAPP_API_ERROR` | error returned by Meta API | ## WABA webhooks (public contract) When project webhook is enabled, Sendeasy sends: 1. `event: "messaging"` for message events. 2. `event: "message_status"` for delivery status updates. Example (`messaging`): ```json { "event": "messaging", "channel": "waba", "data": {}, "channelId": "channel-uuid" } ``` Example (`message_status`): ```json { "event": "message_status", "channel": "whatsapp", "data": { "messageId": "wamid.HBg...", "status": "read", "timestamp": "1713800010" }, "channelId": "channel-uuid" } ``` Common `data.status` values: * `sent` * `delivered` * `read` * `failed` ## Request contact sharing [Request contact sharing](https://docs.sendeasy.app/en/integrations/waba/request-contact.md): Request a phone number through WABA/BSUID or ZuckZapGo/LID using the SendEasy public API. --- # Webhooks (Server) This page describes **what your application receives** in the configured webhooks when Sendeasy processes channel/message events. ## Scopes: general and per-channel Webhooks have **two possible scopes**: * **General**: receives events from **every** channel of the company. Think of it as the "global" webhook for the account. * **Channel-specific**: receives events **only** from the channel it's bound to. The two scopes coexist. For each event, Sendeasy fires the **union**: all active general webhooks of the company **plus** the per-channel webhook(s) for that channel. Configure as many of each scope as you want. Example: a company has 2 WhatsApp instances and 1 Email mailbox. You can set up: * 1 webhook on URL A → only receives Sales WhatsApp events. * 1 webhook on URL B → only receives Support WhatsApp events. * 1 webhook on URL C → only receives Support Email events. Configure under **Settings → Integrations**. ## Flow summary 1. Sendeasy processes a channel/message event. 2. The payload is normalized to the public contract. 3. Sendeasy looks up all `enabled=true` webhooks bound to the source channel. 4. For each webhook found, it `POST`s the URL with the payload as body. If a bearer token was configured, it goes in the `Authorization` header. ## Base payload | Field | Type | Description | | ----------- | ------------------ | -------------------------------------------------------------- | | `event` | `string` | event name | | `channel` | `string` | channel type: `"whatsapp"`, `"email"`, or `"sms"` | | `data` | `object` | event-specific payload | | `channelId` | `string \| number` | channel identifier — UUID (WhatsApp) or numeric id (Email/SMS) | ```json { "event": "messages.upsert", "channel": "whatsapp", "data": {}, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ## Events per channel ### WhatsApp (`channel: "whatsapp"` or `"waba"`) #### `connection.update` ```json { "state": "open" } ``` Common values: `open`, `close`. #### `messages.upsert` / `messages.set` / `send.message` ```json { "key": { "remoteJid": "5511999999999@s.whatsapp.net", "fromMe": false, "id": "3EB0XXXXXX" }, "message": { "conversation": "Hi, I need help" }, "messageTimestamp": "1634567890", "pushName": "Customer" } ``` #### `messages.update` ```json { "keyId": "3EB0XXXXXX", "fromMe": true, "status": 3 } ``` `status`: `1` (sent), `2` (delivered), `3` (read). #### `messages.delete` ```json { "id": "3EB0XXXXXX", "fromMe": false, "remoteJid": "5511999999999@s.whatsapp.net" } ``` #### `call` ```json { "id": "CALL_ID_123", "from": "5511999999999@s.whatsapp.net", "status": "reject" } ``` #### WABA — `messaging` and `message_status` ```json { "event": "message_status", "channel": "whatsapp", "data": { "messageId": "wamid.HBg...", "status": "read", "timestamp": "1713800010" }, "channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } ``` ### Email (`channel: "email"`) Fired when the bound mailbox receives an email. ```json { "event": "messaging", "channel": "email", "channelId": 7, "data": { "email": { "direction": "inbound", "mailbox": "support@company.com", "from": { "email": "customer@domain.com", "name": "Customer" }, "to": ["support@company.com"], "subject": "Question about order #1234", "text": "Hello, I'd like to ask...", "html": "

...

", "messageId": "", "receivedAt": "2026-04-28T13:45:00.000Z" } } } ``` ### SMS (`channel: "sms"`) Fired when the bound number receives an SMS. ```json { "event": "messaging", "channel": "sms", "channelId": 15, "data": { "sms": { "direction": "inbound", "from": "+5511999999999", "body": "Confirm appointment", "providerPayload": { "...": "raw provider data" } } } } ``` ## Consumption best practices 1. Treat processing as asynchronous and implement retry with backoff. 2. Use idempotency keyed on `event` + identifiers (`key.id`, `keyId`, `data.email.messageId`, …). 3. Store the raw payload for troubleshooting. 4. Validate the `Authorization` header when configured on the webhook. 5. Keep timeouts short (≤ 5s) — if Sendeasy doesn't get a response in 5s, the webhook is treated as failed and the event is dropped (no automatic retry). ## Where to manage Webhooks are configured and activated under **Settings → Integrations** on the platform. --- # Web Integration This page covers browser-based integrations for Sendeasy. ## Typical flow 1. Activate API access for the desired channel. 2. Configure allowed origins. 3. Use bearer token in requests. 4. Send messages with JSON or multipart payloads. For production-grade server-to-server integrations, prefer the Server API guides in [/en/server](https://docs.sendeasy.app/en/server.md). --- # Create Bot In this page, you will find a detailed step-by-step guide to create and configure a bot in Sendeasy. # Step-by-step to Create a Bot ## 1. Access your Project * On the Sendeasy home page, **click the project** where you want to create the bot. ## 2. Go to the Bots Section * Once inside the project, you will be redirected to the **project dashboard**. * In the dashboard menu, **click "Bots"**. ## 3. Add a New Bot * In the top-right corner of the Bots page, **click the "+ Add"** button. * This opens a pop-up form to create the bot. ## 4. Fill in Bot Information Fill in the form fields with the required information. Main fields: ### Sector * **Select the sector** where this bot will operate (for example, "Bot Support Sector"). * The bot will automatically reply to all customer messages sent to the selected sector. ### Bot Name * **Provide the bot name**. This field is required and must be unique. * The name is used to identify the bot inside the platform. ### Real-time Data Fetch * **Enable real-time data fetch** when you need the bot to query an external source. * With this option enabled, the bot can fetch a specified URL in real time. * **Supported formats**: XML, JSON, HTML. ⚠️ **Important**: Proxy is not supported. The customer is responsible for improper use of this feature. Make sure the URL is legitimate. ## 5. Save the Bot * **Click "Save"** after filling all fields. * Once saved, the bot becomes active for new chats associated with the selected sector. ## 6. Bot Behavior During Support * When a new chat is associated with the bot sector, it starts pre-support automatically. * **Bot Auto-disable**: if a human agent opens the ticket while the bot is active, it is automatically disabled to prevent overlap. * **Automatic Replies**: otherwise, the bot replies to all customer messages every 10 seconds. **Example**: if the customer sends three messages in sequence ("Hi", "How are you?", "I would like pricing"), and there is no interval longer than 10 seconds between them, the bot replies in sequence, respecting the 10-second interval. *** Follow these steps to configure support bots efficiently and with automation in Sendeasy. For questions or extra support, contact our team through the platform chat. --- # Chat Widget (legacy) The Chat Widget based on integration tokens (`sendeasy-widget-chat-dev.js`, `window.sendeasyConfig`) was replaced by the [Website chat](https://docs.sendeasy.app/widgets/live-chat.md). It is no longer maintained and no longer appears in the panel. Websites still using it keep working with the code already installed. To migrate, create a new website chat in **Website chat > New widget**, paste the generated code and remove the old one. --- # Lead form A form with the fields you choose. Every submission becomes a contact (by phone, by email or new) and a pending ticket in the chosen department, with a message summarizing the answers. ## Installation Create it in **Website chat > New widget > Lead form** and paste the code: ```html ``` ## Modes * **Floating button**: a button in the corner opens the form. * **Automatic pop-up**: opens by itself after the delay, once per visit. * **Inside the page**: the form appears where you place `
`. You can also point to an element with `data-target="#my-element"` on the script tag. Without the element, it falls back to the floating mode. ## Fields Available types: text, email, WhatsApp/phone, long text, option list and checkbox. Each field can be required. Name, email and WhatsApp become the contact details; the others go to the extra information. ## Other settings * Submit button text, message after submit and optional redirect. * Consent (required checkbox with your text). * Labels above the fields and brand in the footer. * Appearance: main color, text color, background color, title, subtitle and position. ## Limits Per token: 10 submissions per minute. Per IP address: 20 submissions every 10 minutes. --- # Website chat Online chat on the website, with bot and agents in real time. The visitor enters a name (and an optional email), the conversation becomes a pending ticket in the chosen department and the history stays in the browser for 7 days. ## Installation Create the widget in **Website chat > New widget > Website chat** and paste the generated code before ``: ```html ``` ## What you can configure * **Service**: widget name, incoming department (where tickets land), allowed websites and the active switch. * **Appearance**: main color, text color, background color, position (right or left), window title and subtitle. * **Texts**: name field, email field, start button, message field and send button, in Portuguese, English and Spanish. The chat follows the visitor's browser language. * **Token**: copy or generate a new one. ## Custom appearance and the code With the default appearance the code is the line above. If you customize colors or texts, the panel generates a block with `window.liveChatConfig` and reminds you to paste the new code. The script also reads the configuration saved in the panel, so on websites with the updated script the change applies even without pasting again. Block format, for reference: ```html ``` Whatever is in `window.liveChatConfig` takes priority over what was saved in the panel. ## Limits Per token: 10 conversations started per minute and 30 messages per minute. Per IP address: 30 conversations every 10 minutes. Above that the visitor sees a message asking to try again shortly. --- # Website widgets Widgets live in the panel under **Website chat** (sidebar or Products). You create the widget, copy one line of code and paste it into your website before the closing `` tag. Anything you change later in the panel applies to the website without pasting the code again. ## The three types | Widget | What it does | Script | | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------- | | [Website chat](https://docs.sendeasy.app/widgets/live-chat.md) | Real-time conversation with bot and agents. Every conversation becomes a ticket. | `widget-live-chat.js` | | [WhatsApp button](https://docs.sendeasy.app/widgets/whatsapp-floating) | Floating button that opens a conversation on your number, with agents, business hours and tracking. | `whatsapp-floating.js` | | [Lead form](https://docs.sendeasy.app/widgets/form) | A form on the website that becomes a contact and a ticket in the panel. | `form-widget.js` | ## How to create one 1. In the panel, open **Website chat** and click **New widget**. 2. Pick the type. 3. Enter a name and the website address (for example `https://mysite.com`). Chat and form accept an incoming department; the WhatsApp button asks for the number. 4. Copy the code and paste it into the website. It is a single line: ```html ``` Replace `widget-live-chat.js` with `whatsapp-floating.js` or `form-widget.js` depending on the type. The panel already generates the right code with your token. ## Allowed websites The widget only opens on pages of the addresses listed under **Allowed websites** (subdomains of the first address also count). Pages outside the list get `ERR_WIDGET_ORIGIN_NOT_ALLOWED` and nothing shows up. To test on another address, add it to the list. ## Token The token identifies the widget. It sits in the installed code, so never publish it in open repositories. To replace it, use **Generate new token** in the widget detail and paste the new code into the website. ## Turning it off without removing the code The **Widget active** switch in the detail turns the widget off on the website immediately, without touching the installed code. ## Legacy Chat Widget The Chat Widget based on integration tokens (`sendeasy-widget-chat-dev.js`) was replaced by the website chat and is no longer maintained. Websites still using it keep working, but the recommendation is to migrate by creating a new website chat in the panel. --- # WhatsApp button Fixed button in the corner of the website that opens a conversation on your WhatsApp with the message already typed. It does not create a channel or a ticket: the conversation happens on WhatsApp. ## Installation Create it in **Website chat > New widget > WhatsApp button**, enter the number with country and area code and paste the code: ```html ``` Everything you configure in the panel applies to the website without pasting the code again. ## What you can configure * **WhatsApp**: main number, opening message, include the page in the message and linked channel (panel reference only). * **Button**: text, button color, icon and text color, delay before showing, show on all devices, mobile only or desktop only, call-out bubble with text. * **Agents**: up to 10, with name, role and number. With more than one, the click opens a list for the visitor to choose; each agent can have its own opening message. * **Business hours**: days, start, end and time zone. Outside the hours the button stays, but shows a notice. * **Tracking (UTM)**: adds `utm_source`, `utm_medium` and `utm_campaign` to the page address included in the message. * **Position**: right or left.