# Make Agent Fast documentation (en) --- # Documentation Source: /docs.md Make Agent Fast gives you one place to create an agent, connect its knowledge, publish it, and meet customers wherever they already are. ## Start here [#start-here] Pick a path based on what you want to ship first. ## The basic workflow [#the-basic-workflow] Describe the agent you need or start from a template. Add website pages, files, FAQs, products, and instructions. Use a Make Agent Fast page, your own domain, an embed, or a connector. Review conversations, analytics, leads, usage, and agent behavior. Every page is available as Markdown. Open the page action menu to copy or download it, or send its canonical Markdown URL to ChatGPT or Claude. The complete machine-readable corpus is available at [/docs/llms-full.txt](/docs/llms-full.txt). ## What you can build [#what-you-can-build] ## Get help [#get-help] If a guide does not answer your question, use the feedback control at the bottom of the page or contact support from your dashboard. For product limits and billing, see [Plans and pricing](/docs/account/pricing). --- # Changelog Source: /docs/account/changelog.md {/* docs-visuals */} ## 2026-07-28 [#2026-07-28] * Simplified usage history to show final credits consumed instead of separate reserve and refund ledger rows; tiny usage now displays as `<0.001`. * Made documentation action-menu selection corners adapt to multiline content while keeping the trigger and menu shell geometry matched. * Expanded English, Korean, and Uzbek guides for live-call billing, visual-builder media input, hosted subdomain changes, account privacy/security controls, and the Notifications event feed. ## 2026-07-15 [#2026-07-15] * Introduced the bilingual Make Agent Fast documentation platform. * Added per-page Markdown, downloads, search, ChatGPT and Claude actions. * Added `/docs/llms.txt`, `/docs/llms-full.txt`, and canonical `.md` routes. * Documented the public API, SDK contract, webhooks, embeds, connectors, operations, pricing, security, and privacy. * Added implementation guides for major frameworks and website builders. ## Versioning policy [#versioning-policy] Documentation corrections and additive API fields may ship continuously. Breaking public API changes require a new major path or a documented migration period. --- # Plans and pricing Source: /docs/account/pricing.md {/* docs-visuals */} ## Plan comparison [#plan-comparison] | Plan | Monthly billing | Annual total | Monthly credits | Sites | Live voice | Voice clones | Branding badge | | ------- | --------------: | -----------: | --------------: | ------------------: | -----------: | -----------------: | -------------- | | Starter | $15/month | $144/year | 100 | 1 | Not included | Not included | Required | | Pro | $35/month | $336/year | 300 | 3 | 30 min/month | 1 | Removable | | Studio | $79/month | $758.40/year | 800 | Unlimited, fair use | 90 min/month | Multiple, fair use | Removable | Annual billing is 20% below paying the monthly price twelve times. Starter and Pro begin with a seven-day card-required trial; Studio begins billing immediately and has no trial. The checkout shows the exact price, renewal period, tax, and trial terms before purchase. ## Included on every plan [#included-on-every-plan] All current plans include: * A hosted site on `YOUR_SLUG.makeagent.fast`. * Text chat, voice messages, stock voices, knowledge retrieval, structured FAQs/products, and lead capture. * Website embedding with exact origin controls. * Supported messaging connectors using credentials you provide. * Public API access with scoped keys, OAuth for reviewed apps, OpenAPI, and plan-scaled rate limits. * Analytics, conversations, usage tracking, and the ability to buy credit packs. Starter's Public API allowance is 120 requests per API key per minute, Pro's is 600, and Studio's is 1,800. These request limits do not replace resource entitlements, credit checks, idempotency, or provider-specific limits. See [Rate limits](/docs/api/rate-limits). ## Understand credits [#understand-credits] Credits pay for metered AI work such as generation, agent turns, knowledge embeddings, transcription, speech synthesis, and cloning. They are not a fixed number of messages: provider, model, input/output length, media duration, and operation type change the cost. Monthly plan credits reset with the allowance period and unused monthly allowance expires. Purchased top-up credits do not expire under the current product rules. A positive credit balance does not unlock a plan-only feature such as live calls, extra sites, cloning, or badge removal. Bring-your-own-provider keys can create separate charges from OpenAI, Anthropic, Google, Deepgram, or ElevenLabs. BYOK does not convert provider invoices into Make Agent Fast credits or eliminate platform entitlement checks. See [AI provider keys](/docs/account/provider-keys). ## Voice entitlements [#voice-entitlements] Voice messages are included on all plans but their transcription, model, and speech work can consume credits. Live real-time voice uses a separate monthly minute allowance: none on Starter, 30 minutes on Pro, and 90 minutes on Studio. Voice cloning is Pro/Studio only. Pro includes one workspace clone; Studio supports multiple under fair use. Re-cloning a site that already owns a clone replaces it rather than consuming a new slot. Cloning also requires explicit consent, provider availability, and enough credits for the operation. ## Sites and branding [#sites-and-branding] Starter supports one site and Pro supports up to three. Studio's unlimited-sites description is subject to fair use rather than an invitation to create abusive or automated tenant volume. A site includes its agent, knowledge, public hosted experience, embed configuration, connectors, analytics, and conversation data. The “Made with Make Agent Fast” referral badge remains on Starter. Pro and Studio owners can remove it from entitled published sites. ## Add-ons and top-ups [#add-ons-and-top-ups] | Purchase | Price | What it changes | | ------------- | ---------------------------: | ------------------------------------------------------------------------ | | Custom domain | $2/month per configured site | Adds one verified domain entitlement; separate from the plan and credits | | Small pack | $8 once | Adds 100 non-expiring credits | | Medium pack | $35 once | Adds 500 non-expiring credits | | Large pack | $90 once | Adds 1,500 non-expiring credits | A custom-domain add-on is purchased after free DNS ownership verification. Removing the domain cancels its associated add-on; changing to another domain requires removing the existing site domain first. Provider capacity may temporarily prevent a new checkout. Top-ups add usage capacity only. They do not increase site count, API request limits, live minutes, clone count, custom domains, or branding permissions. ## Trials, renewal, and cancellation [#trials-renewal-and-cancellation] A card-required trial converts to the selected paid subscription unless canceled before the trial ends according to the checkout terms. Canceling or a failed renewal can remove active access after the applicable provider period/status transition. Public sites, custom domains, connectors, API clients, and voice calls may stop even though stored workspace data remains. The custom-domain add-on has its own billing status. Restoring the platform plan does not necessarily repair a lapsed domain add-on until its billing is also resolved. ## Choose a plan [#choose-a-plan] * Choose **Starter** for one hosted/embedded agent with text and asynchronous voice messages. * Choose **Pro** when you need up to three sites, live calls, one voice clone, or badge removal. * Choose **Studio** for a multi-site operation needing higher credits/API throughput, 90 live minutes, and multiple clones. Estimate from real acceptance tests rather than message count alone. Publish one representative agent, measure text/voice/knowledge usage, then project the traffic and add headroom for retries, connectors, automation, and launch peaks. ## Billing source of truth [#billing-source-of-truth] The live checkout and billing dashboard are the source of truth for a specific purchase. Taxes, currency presentation, payment methods, trial eligibility, and provider wording can vary by location and account state. If a checkout differs from this page, stop and use the checkout terms or contact support before paying. --- # Privacy and data Source: /docs/account/privacy.md {/* docs-visuals */} The site owner decides which knowledge, visitor inputs, and integrations are used for their experience. Make Agent Fast processes platform data to provide the service, subject to the applicable terms and privacy notice. ## Visitor notice [#visitor-notice] Explain AI use, recording, lead capture, analytics, and follow-up in language appropriate to the experience and local requirements. Do not ask the agent to collect data you do not need. ## Data lifecycle [#data-lifecycle] Removing a source affects future retrieval. Deleting a credential stops authorized access. Historical conversations, exports, external channel data, and provider records can have separate retention behavior. ## Owner controls [#owner-controls] The site **Analytics** page can export captured leads as JSON and delete an individual lead. Analytics reports can also be exported as Markdown, Excel, or a print/PDF view. An export creates a new copy under your control; deleting the platform record does not erase an earlier download, a related conversation, an external-provider record, or data retained for a lawful requirement. Use **Account settings** to sign out every device or permanently delete the account. Account deletion first attempts to stop the platform subscription, custom-domain add-ons, and connector resources, then removes the account's sites, agents, knowledge, conversations, leads, API/OAuth credentials, sessions, and locally stored billing history. If external billing cannot be confirmed as stopped, deletion fails closed and leaves the account intact for a safe retry or support review. ## Requests [#requests] Use the self-service controls where they match the request. For a broader access, correction, export, or deletion request, use account support and the process described in the current Privacy Policy. Verify the requester's identity before disclosing account data, and include downstream exports and external providers in the response plan. --- # AI provider keys Source: /docs/account/provider-keys.md {/* docs-visuals */} Bring your own key (BYOK) lets supported AI requests use your account with the AI provider. You still need an active Make Agent Fast plan, and usage still consumes platform infrastructure credits. The provider bills its model usage directly to you. ## Supported providers [#supported-providers] | Provider | Used for | Create or review a key | | ------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | Anthropic | Agent text generation | [Anthropic API documentation](https://docs.anthropic.com/en/api/getting-started) | | OpenAI | Agent text generation and configured speech modes | [OpenAI API quickstart](https://platform.openai.com/docs/quickstart/make-your-first-api-request) | | Google Gemini | Agent text generation and configured realtime mode | [Gemini API keys](https://ai.google.dev/gemini-api/docs/api-key) | | Deepgram | Speech-to-text when Deepgram is the configured STT provider | [Deepgram API keys](https://developers.deepgram.com/docs/create-additional-api-keys) | | ElevenLabs | Text-to-speech and voice cloning when ElevenLabs is configured | [ElevenLabs authentication](https://elevenlabs.io/docs/api-reference/authentication) | Provider availability also depends on the platform's configured text, speech, realtime, and cloning providers. Saving a valid key does not force every feature to use that provider. A provider key authorizes charges and model calls at the external AI provider. A `maf_live_...` key authorizes your software to call the Make Agent Fast public API. Never paste one into the other field. ## Add a provider key [#add-a-provider-key] 1. Create a restricted key in the provider's console and enable provider billing or quota if required. 2. Open **Dashboard → Settings → AI provider keys**. 3. Choose the matching provider and add a label that identifies its project or environment. 4. Paste the key and save it. 5. Wait for the validation result, then test the feature that uses the provider. Make Agent Fast performs a live validation request before marking a key active. The key must contain no spaces or line breaks and must be between 16 and 512 characters. Anthropic keys must start with `sk-ant-`; OpenAI keys must start with `sk-`. Other providers do not have a stable public prefix, so live validation is authoritative. Saving another key for the same provider replaces the previous stored value. The dashboard retains only safe identification data such as the label, last four characters, validation time, and state. ## How text-provider selection works [#how-text-provider-selection-works] For text generation, Make Agent Fast first looks for an active BYOK key matching the platform's configured text provider. If none exists, it checks active Anthropic, OpenAI, and Gemini keys in that order. If no suitable BYOK key exists, it uses the platform provider when the plan and deployment allow it. Speech keys are provider-specific. A Deepgram key is used only when the active speech-to-text provider is Deepgram; an ElevenLabs key is used only when text-to-speech or cloning is configured for ElevenLabs. BYOK keys do not currently replace the platform's knowledge-embedding credentials. Knowledge ingestion and retrieval continue using the deployment's configured embedding provider. ## Billing and credits [#billing-and-credits] * The external provider charges usage to the account that issued the key. * Make Agent Fast still charges the subscription and infrastructure portion of supported BYOK work. * Text BYOK requests normally use the reduced infrastructure meter rather than the full model-cost meter. * Voice work can retain speech-pipeline minimums because capture, transcription, synthesis, delivery, and orchestration are separate stages. * Provider quota exhaustion can fail a request even when Make Agent Fast credits remain. Review both **Usage & credits** in Make Agent Fast and the provider's own usage dashboard. ## Key states and failure handling [#key-states-and-failure-handling] | State | Meaning | What to do | | --------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------ | | `active` | Live validation succeeded | Test the target feature and monitor provider usage | | `invalid` | Validation failed or the provider later rejected it | Check provider billing, restrictions, quota, and revocation state; then save a replacement | | `revoked` | The key is disabled in Make Agent Fast | Create or activate a replacement if the feature should continue using BYOK | If a request fails because a provider rejects the credential or quota, Make Agent Fast can mark the stored key invalid. Fixing the key at the provider does not reveal or rewrite the stored secret; save the working key again to revalidate it. ## Rotate a provider key [#rotate-a-provider-key] 1. Create a new provider key without deleting the old one. 2. Save the new key in Make Agent Fast and confirm it becomes active. 3. Test text chat, voice messages, or cloning—whichever uses that provider. 4. Revoke the old key in the provider console. 5. Check provider logs for unexpected use of the old key. If a key is exposed in source control or browser code, revoke it at the provider immediately. Deleting it only from Make Agent Fast does not stop another holder from using it directly with the provider. --- # Security Source: /docs/account/security.md {/* docs-visuals */} ## Account access [#account-access] Use a unique password, protect the email account used for recovery, end sessions you do not recognize, and limit who can access production sites. Verify the account email from the link sent after registration. Password accounts can use **Forgot password** on the login page; a successful reset revokes existing sessions. **Account settings** can change a local password, link Google explicitly when enabled, and sign out every device. OAuth-only accounts do not have a local password to change, and a normal login never silently links a new identity. ## Secrets [#secrets] API keys, connector tokens, app secrets, webhook secrets, and voice-provider keys are credentials. Store them in an approved secret manager, never in client code, and rotate after exposure. ## Least privilege [#least-privilege] Choose narrow API scopes, dedicated external-platform apps, precise embed origins, and separate keys for separate systems. Remove unused credentials and integrations. ## Content and privacy [#content-and-privacy] Upload only data the agent is permitted to use. Test refusals and escalation paths, and apply retention rules to conversation and lead exports. ## Reporting [#reporting] When reporting a suspected vulnerability, include the affected URL or component, impact, and reproduction steps without accessing other users' data. --- # Troubleshooting Source: /docs/account/troubleshooting.md {/* docs-visuals */} ## Start with a reproducible case [#start-with-a-reproducible-case] Before changing settings, capture: * The workspace/site, public route or API endpoint, and exact time with timezone. * Expected result and actual result. * Browser/device or external provider channel. * Whether the failure affects the owner preview, hosted site, custom domain, embed, connector, and/or API. * Safe status/error text, response `x-request-id`, connector last error, or provider delivery event ID. * The last known working time and the smallest recent change. Do not include plaintext API/provider/connector credentials, webhook secrets, private customer data, full authorization headers, or unnecessary conversation content. ## Site changes do not appear [#site-changes-do-not-appear] 1. Confirm the dashboard URL belongs to the intended site and workspace. 2. Confirm the save action reported success. 3. Publish the current state again. 4. Open the hosted slug in a private window. 5. If the hosted slug is correct but a custom domain is not, inspect domain state/DNS/TLS rather than editing content again. 6. If only a social preview is old, use that platform's cache/debugger. Unpublishing and editing are different. A connected messaging connector may continue answering even when the hosted page is unpublished. ## Agent answer is weak or wrong [#agent-answer-is-weak-or-wrong] Ask the identical question in the owner tester and a new public conversation. Confirm the relevant knowledge source was indexed, contains an explicit answer, is current, and does not conflict with another source. Check language/persona rules for a boundary that prevents the answer. When the source has no answer, improve the fallback/escalation behavior instead of instructing the agent to guess. Use [Knowledge sources](/docs/build/knowledge) to create a direct/paraphrase/boundary acceptance set. ## Embed launcher is missing or blocked [#embed-launcher-is-missing-or-blocked] Confirm the site is published, embedding is enabled, the script has the exact site slug, and the browser's final parent origin—including scheme, hostname, and port—is allowlisted. In DevTools, inspect the `embed.js` request, CSP errors, consent-manager state, iframe request, and presence of one `data-maf-embed` root. If text works but microphone does not, check HTTPS, browser permission, iframe allow permission, and the host Permissions Policy. Follow the complete [Embed overview](/docs/embed/overview) and [Security and CSP](/docs/embed/security). ## Voice does not work [#voice-does-not-work] Separate the failing mode: voice message, live call, clone, or connector voice. A working text path proves only the language-model key. Check transcription, TTS, real-time, and cloning provider keys independently; then check plan entitlement, credit balance, live-minute allowance, browser permission, configured languages, sample limits, and consent. Test a short clean recording before investigating a long/noisy file. ## Connector receives no messages [#connector-receives-no-messages] Use the channel guide and compare both sides. Confirm the connector is enabled, credentials still validate, provider webhook/interaction/skill URL is the current one, required event/Page/number/block is subscribed, app mode includes the test user, and public HTTPS delivery succeeds. Webhook verification success does not prove runtime event subscription or outbound permissions. Compare provider delivery logs with **Last message** and the connector's saved error. Test text before voice. ## Custom domain does not activate [#custom-domain-does-not-activate] Read the status before changing DNS: * **Pending DNS:** ownership TXT is missing or not propagated. * **Verified:** ownership passed but the $2/month add-on is not active. * **Attaching:** add every displayed apex/`www` record and remove conflicts. * **Error:** use the saved provider message and retry action. * **Payment required/Suspended:** repair the domain add-on or platform plan. The hosted slug is the fallback diagnostic surface. If it also fails, the problem is not only DNS. See [Custom domains](/docs/publish/custom-domains). ## API request fails [#api-request-fails] Record method, versioned path, status, stable error code, response `x-request-id`, and relevant rate-limit headers. Then classify: | Status | Most likely action | | ------ | ------------------------------------------------------------------------- | | `400` | Fix JSON/form shape or validation details; do not retry unchanged | | `401` | Use a valid unexpired/unrevoked MAF credential; never use a provider key | | `403` | Add the required scope or restore plan/resource access | | `404` | Confirm the ID belongs to the authenticated workspace and path is correct | | `409` | Resolve state conflict or replay the same idempotency key/payload | | `415` | Send the documented content type | | `429` | Honor `retry-after`/reset headers with jitter; do not busy-loop | | `500` | Retry a safe/idempotent request and preserve the request ID/time | Use [Credentials and tokens](/docs/api/credentials), [Scopes and errors](/docs/api/scopes-errors), and [Rate limits](/docs/api/rate-limits). Never solve an API error by moving a server credential into browser code. ## Billing or usage looks wrong [#billing-or-usage-looks-wrong] Distinguish platform access, monthly credits, purchased credits, site count, live minutes, clone slots, API rate, and custom-domain add-on status. A top-up changes credits only. BYOK provider charges are separate from Make Agent Fast billing. For unexpected usage, contain the likely source before investigating: disable a connector, revoke a key, remove an unintended embed origin, or stop an API retry loop. Preserve time range and request/provider IDs without exposing secrets. ## Collect safe support details [#collect-safe-support-details] If still blocked, use the feedback control on the most relevant guide or contact support from the dashboard with: * Site/workspace identifier and affected feature. * Exact timestamp/timezone and reproducible steps. * Expected vs actual result. * Public non-secret URL when relevant. * HTTP status, stable error code, `x-request-id`, or provider event ID. * Redacted screenshot or minimal payload with secrets and personal data removed. State what you already checked. This prevents repeated setup advice and makes the failure boundary clear. --- # Authentication Source: /docs/api/authentication.md {/* docs-visuals */} Use an API key for your own backend or automation. Use OAuth when your application asks other Make Agent Fast users to grant access. Both credential types call the same `/api/v1` resources and obey the same scopes, plan entitlements, tenant isolation, and rate limits. ## API keys [#api-keys] An active paid plan is required to create and use a public API key. 1. Open **Dashboard → Settings → Developer**. 2. Enter a name that identifies the service and environment, such as `Production lead export`. 3. Select the minimum scopes the service needs. 4. Choose an expiry: 30 days, 90 days, one year, or never. 5. Create the key and copy the `maf_live_...` value immediately. Make Agent Fast stores a SHA-256 hash rather than recoverable plaintext. The dashboard later shows a safe prefix, scopes, expiry, last-used time, and revocation state. ```http Authorization: Bearer maf_live_... ``` Send the header from server code: ```bash curl --fail-with-body https://makeagent.fast/api/v1/me \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Accept: application/json" ``` Missing credentials return `401 authentication_required`. Invalid, expired, or revoked keys return `401 invalid_api_key`. A key whose account no longer has an active paid plan returns `403 subscription_required`. ### Rotate or revoke a key [#rotate-or-revoke-a-key] Create a replacement before revoking the old key. Deploy the replacement, make a real request, verify its `x-request-id`, then click **Revoke** on the old key. Revocation is immediate and cannot be undone. Scopes and expiry cannot be broadened on an existing key. Create a replacement so the permission change is explicit. ## OAuth applications [#oauth-applications] OAuth is for software connecting accounts owned by other Make Agent Fast users. It uses Authorization Code with PKCE (`S256`) and does not issue or require a client secret. The client ID is public; authorization codes, access tokens, refresh tokens, and the PKCE verifier are sensitive. ### 1. Register the application [#1-register-the-application] Open **Dashboard → Settings → Developer → OAuth applications**. Add: * An application name from 2 to 100 characters. * Between 1 and 10 exact redirect URLs. * The maximum scopes your application may request. Production redirect URLs must use HTTPS and cannot contain credentials or fragments. The complete URI must match a registered value during authorization and token exchange. Newly submitted applications have `pending` status; authorization works only after the application is reviewed and marked `approved`. The platform owner reviews pending client IDs with the audited `oauth:review` workflow. Keep the client ID shown after submission; its status is visible on the Developer page, and rejected applications include the review decision before they can be resubmitted. ### 2. Generate PKCE values [#2-generate-pkce-values] Generate a new verifier for every authorization attempt and keep it in the same protected, short-lived session as `state`. ```ts import { createHash, randomBytes } from "node:crypto"; const verifier = randomBytes(48).toString("base64url"); const challenge = createHash("sha256").update(verifier).digest("base64url"); const state = randomBytes(24).toString("base64url"); ``` The verifier must contain 43–128 URL-safe PKCE characters. Only `code_challenge_method=S256` is accepted. ### 3. Redirect the user for consent [#3-redirect-the-user-for-consent] ```text https://makeagent.fast/oauth/authorize ?client_id=maf_app_... &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback &response_type=code &scope=sites%3Aread%20leads%3Aread &state=RANDOM_STATE &code_challenge=PKCE_CHALLENGE &code_challenge_method=S256 ``` The user signs in, reviews the requested scopes, and allows or denies access. On success, Make Agent Fast redirects to the exact registered URI with `code` and the original `state`. On denial it sends `error=access_denied` and the original `state` when one was supplied. Reject the callback if `state` does not exactly match the value stored for that browser session. ### 4. Exchange the authorization code [#4-exchange-the-authorization-code] The authorization code expires after 10 minutes and can be used only once. ```bash curl --fail-with-body https://makeagent.fast/api/oauth/token \ -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "client_id=maf_app_..." \ --data-urlencode "code=$AUTHORIZATION_CODE" \ --data-urlencode "redirect_uri=https://app.example.com/oauth/callback" \ --data-urlencode "code_verifier=$PKCE_VERIFIER" ``` ```json { "access_token": "maf_live_...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "maf_refresh_...", "scope": "sites:read leads:read" } ``` The access token lasts one hour. The refresh token lasts up to 90 days unless it is rotated or revoked earlier. ### 5. Rotate the refresh token [#5-rotate-the-refresh-token] Every successful refresh revokes both the submitted refresh token and its previous access token, then returns a new pair. Save the new pair atomically before discarding the previous response state. ```bash curl --fail-with-body https://makeagent.fast/api/oauth/token \ -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "client_id=maf_app_..." \ --data-urlencode "refresh_token=$REFRESH_TOKEN" ``` Reusing an authorization code or rotated refresh token returns `invalid_grant`. The OAuth token endpoint returns OAuth-style `{ "error", "error_description" }` responses rather than the public API error envelope and allows 30 token requests per client and caller per minute. ## Never authenticate management requests in browser code [#never-authenticate-management-requests-in-browser-code] Do not put a secret API key, access token, or refresh token in React client components, static JavaScript, mobile web, Webflow, Framer, Wix, or Shopify Liquid. Use your authenticated backend. The public website embed uses a non-secret site slug and origin allowlist, not management API credentials. See [Credentials and tokens](/docs/api/credentials), [Scopes and errors](/docs/api/scopes-errors), and [API quickstart](/docs/api/quickstart). --- # Credentials and tokens Source: /docs/api/credentials.md {/* docs-visuals */} Make Agent Fast uses several credential types. They are not interchangeable. Before copying a value, identify which system issued it, which system receives it, and whether it is safe to expose in a browser. ## Credential map [#credential-map] | Credential | Created in | Used by | Secret? | Typical format or value | | ----------------------------- | -------------------------------------------------- | --------------------------------- | ------- | --------------------------------- | | Published site slug | Site dashboard | Website embed loader | No | `my-agent` | | Make Agent Fast API key | **Settings → Developer** | Your backend or automation | Yes | `maf_live_...` | | OAuth client ID | **Settings → Developer** | Multi-account application | No | `maf_app_...` | | OAuth access token | OAuth token endpoint | Your application backend | Yes | Bearer token, one-hour lifetime | | OAuth refresh token | OAuth token endpoint | Your secure token store | Yes | `maf_refresh_...`, rotated on use | | AI-provider API key | Anthropic, OpenAI, Google, Deepgram, or ElevenLabs | Make Agent Fast backend | Yes | Provider-specific | | Connector token or app secret | Telegram, Meta, Discord, or Kakao | Make Agent Fast connector service | Yes | Provider-specific | | Webhook signing secret | Public API webhook endpoint creation | Your webhook receiver | Yes | `whsec_...` | The embed loader needs a public site slug and an allowed website origin. Never add a Make Agent Fast API key, provider key, connector token, or webhook secret to the script tag. ## Choose the right authorization method [#choose-the-right-authorization-method] Use a **Make Agent Fast API key** for a server you control and a single Make Agent Fast account. Choose only the scopes that server needs. Use **OAuth Authorization Code with PKCE** when your product connects accounts belonging to other Make Agent Fast users. OAuth applications require review before production authorization succeeds. Use an **AI-provider key** only when you want Make Agent Fast to call that AI provider against your provider account. A provider key does not authorize the Make Agent Fast public API. Use **connector credentials** only in the matching connector form or connector API request. A Telegram bot token cannot authorize Discord, Meta, or Make Agent Fast API requests. ## Store secrets safely [#store-secrets-safely] For local development, place secrets in an ignored environment file or your shell environment: ```bash export MAF_API_KEY="maf_live_..." ``` For deployment, use the hosting provider's encrypted secret manager. Do not put secrets in: * Git commits, issue descriptions, screenshots, analytics properties, or support messages. * `NEXT_PUBLIC_*`, `VITE_*`, or other variables bundled into browser JavaScript. * Webflow, Framer, Wix, Shopify Liquid, WordPress HTML, or a mobile-web bundle. * Query strings. URLs are commonly retained in browser history, proxies, and logs. Make API calls through your authenticated backend. The browser should call your backend, and your backend should attach the secret. ```ts const response = await fetch("https://makeagent.fast/api/v1/sites", { headers: { Authorization: `Bearer ${process.env.MAF_API_KEY}` }, }); ``` ## One-time secret rules [#one-time-secret-rules] API keys, webhook signing secrets, and newly issued OAuth refresh tokens must be copied when shown. The full plaintext value cannot be recovered later. If it is lost, create a replacement rather than asking support to reveal it. AI-provider and connector credentials are encrypted before storage and are not returned through list APIs. The dashboard may show a label, prefix, last four characters, validation state, or last-used time so you can identify a credential without exposing it. ## Rotation checklist [#rotation-checklist] 1. Create the replacement with the same minimum scopes or provider permissions. 2. Update one deployment or receiver at a time. 3. Verify a real request succeeds and record its `x-request-id`. 4. Revoke the old credential. 5. Confirm old requests fail with `401 invalid_api_key` or the provider's equivalent. For OAuth, store the new refresh token returned by every refresh response before discarding the previous token. For webhook-secret rotation, run old and new endpoints in parallel until every producer and receiver uses the replacement. ## If a secret is exposed [#if-a-secret-is-exposed] Revoke or rotate it immediately. Removing it from the latest Git commit is not sufficient because it may remain in history, caches, logs, or forks. Review activity at both Make Agent Fast and the issuing provider, then replace any credential that shared the same storage location or deployment environment. Continue with [Authentication](/docs/api/authentication), [Scopes and errors](/docs/api/scopes-errors), or [AI provider keys](/docs/account/provider-keys). --- # API overview Source: /docs/api/overview.md {/* docs-visuals */} The public API uses resource-oriented JSON over HTTPS under `/api/v1`. It is available on paid plans and uses the same site, credit, and feature entitlements as the dashboard. ## What you can automate [#what-you-can-automate] * Sites and publishing state * Agent configuration, FAQs, products, and knowledge * Conversations, leads, analytics, notifications, and broadcasts * Domains, connectors, monetization, usage, and account information * Signed webhook subscriptions for supported events Sensitive actions use explicit action endpoints rather than unrestricted database mutation. Connector secrets are accepted on write but never returned. This is currently a **management and synchronization API**. Conversations, leads, analytics, and usage are read-only; v1 does not expose a visitor chat send, owner reply, or lead-create endpoint. Use the published widget/embed for visitor conversations and the dashboard for owner follow-up. ## Start safely [#start-safely] Create a scoped key in the dashboard, call the current-account endpoint, then build against a test site before touching production content. ```bash curl https://makeagent.fast/api/v1/me \ -H "Authorization: Bearer maf_live_YOUR_KEY" ``` See [Authentication](/docs/api/authentication), [API quickstart](/docs/api/quickstart), and the [API reference](/docs/api/reference). --- # API quickstart Source: /docs/api/quickstart.md {/* docs-visuals */} This quickstart verifies that a backend can authenticate and read your account. You need an active paid plan, a terminal with `curl`, and access to **Dashboard → Settings → Developer**. The current v1 API manages and synchronizes resources. It is not a custom-chat transport: conversation and lead endpoints are read-only. ## 1. Create a least-privilege key [#1-create-a-least-privilege-key] Create a key named `Local API quickstart`, select `account:read` and `sites:read`, and choose a short expiry. Copy the plaintext key immediately; it will not be shown again. Do not use a provider key from OpenAI, Anthropic, Gemini, Deepgram, or ElevenLabs. The public API requires a Make Agent Fast key beginning with `maf_live_`. ## 2. Store it for this terminal [#2-store-it-for-this-terminal] ```bash export MAF_API_KEY="maf_live_..." ``` Confirm the variable exists without printing the secret: ```bash test -n "$MAF_API_KEY" && echo "MAF_API_KEY is set" ``` Do not commit the key to `.env`, shell-history examples, test fixtures, or a browser bundle. For deployed code, use the host's secret manager. ## 3. Inspect the account [#3-inspect-the-account] ```bash curl --fail-with-body --include https://makeagent.fast/api/v1/me \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Accept: application/json" ``` Expected status: `200 OK`. The body contains `data.id`, plan and entitlement information, and a wallet summary. The response also contains `x-request-id`; retain that value when reporting a failed request. ```json { "data": { "id": "ACCOUNT_ID", "email": "owner@example.com", "plan": { "id": "pro", "status": "active" }, "entitlements": { "site_limit": 3, "live_voice": true }, "wallet": { "balance": 151 } } } ``` Fields can be added without changing the `/v1` path. Ignore unknown fields instead of rejecting the response. ## 4. List sites [#4-list-sites] ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites?limit=20" \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Accept: application/json" ``` Collection responses use this envelope: ```json { "data": [], "has_more": false, "next_cursor": null } ``` If `has_more` is true, send the exact `next_cursor` value as the next request's `cursor` query parameter. Do not decode or construct cursors. ## 5. Handle a structured error [#5-handle-a-structured-error] Temporarily remove the Authorization header and repeat `/me`. The response should be `401` with a stable code: ```json { "error": { "code": "authentication_required", "message": "Provide an API key in the Authorization bearer header.", "request_id": "6c0b2f2e-..." } } ``` Production code should branch on `error.code`, not compare `message`. Log the operation, HTTP status, and request ID without logging the key. ## 6. Make the first write safely [#6-make-the-first-write-safely] Create a separate key that also has `sites:write`, `agents:write`, and `knowledge:write`; do not expand the quickstart read key. Follow [API recipes](/docs/api/recipes) to create a site, add grounded knowledge, and publish it using explicit idempotency keys. ## Common first-request failures [#common-first-request-failures] | Result | Cause | Fix | | ----------------------------- | -------------------------------------------------------- | --------------------------------------------------- | | `401 authentication_required` | Header missing or not `Bearer TOKEN` | Add the exact Authorization header from server code | | `401 invalid_api_key` | Typo, expired key, revoked key, or wrong credential type | Create and copy a new Make Agent Fast API key | | `403 subscription_required` | No active paid plan | Restore the account subscription | | `403 insufficient_scope` | Key does not include the operation's scope | Create a replacement with the minimum missing scope | | `403 account_paused` | API access was paused for this account | Resume API access or contact the account owner | | `429 rate_limit_exceeded` | One-minute key bucket exhausted | Wait for `Retry-After` and reduce concurrency | Continue with [Scopes and errors](/docs/api/scopes-errors), [API reference](/docs/api/reference), and [Rate limits](/docs/api/rate-limits). --- # Rate limits Source: /docs/api/rate-limits.md {/* docs-visuals */} The public API applies a rolling one-minute request bucket to each API key or OAuth access token. Creating more keys to evade a limit is unsupported and can lead to credential or account restrictions. ## Plan limits [#plan-limits] | Active plan | Requests per key per minute | | ----------- | --------------------------: | | Starter | 120 | | Pro | 600 | | Studio | 1,800 | A credential whose account has no active paid plan receives `403 subscription_required` before rate-limit capacity is granted. Make Agent Fast can introduce lower endpoint-specific limits for unusually expensive operations; when that happens, the endpoint reference will identify them explicitly. ## A rate-limited response [#a-rate-limited-response] When the bucket is exhausted, the API returns `429 rate_limit_exceeded` with: ```http HTTP/1.1 429 Too Many Requests Retry-After: 17 X-RateLimit-Limit: 600 X-RateLimit-Remaining: 0 X-Request-Id: 6c0b2f2e-... Content-Type: application/json ``` `Retry-After` is the minimum number of seconds to wait. Every authenticated response also includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (a Unix timestamp), so applications can throttle before a `429`. ## Retry implementation [#retry-implementation] Use exponential backoff with random jitter, honor a larger `Retry-After`, and cap the total number of attempts. ```ts async function requestWithRetry(url: string, init: RequestInit, attempts = 4) { for (let attempt = 0; attempt < attempts; attempt += 1) { const response = await fetch(url, init); if (response.status !== 429 && response.status < 500) return response; if (attempt === attempts - 1) return response; const retryAfter = Number(response.headers.get("retry-after") ?? 0) * 1_000; const exponential = 500 * 2 ** attempt; const jitter = Math.random() * 250; await new Promise((resolve) => setTimeout(resolve, Math.max(retryAfter, exponential + jitter)), ); } throw new Error("unreachable"); } ``` For `POST`, `PATCH`, and `DELETE`, keep the same valid `Idempotency-Key` across every network or server retry. A new idempotency key can repeat a completed side effect. ## Reduce request volume [#reduce-request-volume] * Cache account, site, and configuration reads that do not need real-time freshness. * Request up to `100` records per page instead of repeatedly requesting very small pages. * Process webhook events instead of polling conversations, leads, domain state, or broadcasts. * Bound worker concurrency per API key; a burst from many serverless invocations shares the same bucket. * Use separate keys for separate services for isolation and auditability, not to multiply one workload's capacity. ## Retry and do-not-retry table [#retry-and-do-not-retry-table] | Response | Retry? | Condition | | --------------------------------- | --------------- | ---------------------------------------------------------------------- | | `429` | Yes | Wait for `Retry-After`, add jitter, and cap attempts | | `500` | Sometimes | Safe reads or mutations protected by the same idempotency key | | `409` processing conflict | Yes | Wait for its `Retry-After: 2` header and keep the same idempotency key | | Other `409` | Not immediately | Read the current resource state and resolve the conflict | | `400`, `401`, `403`, `404`, `415` | No | Correct input, credentials, entitlement, path, or content type first | If a sustained workload legitimately exceeds its plan bucket, reduce polling, batch work, or contact support with representative request IDs and expected traffic rather than adding an unbounded retry loop. --- # API recipes Source: /docs/api/recipes.md {/* docs-visuals */} These recipes use raw REST so they work before a public SDK is installed. Set `MAF_API_KEY` in the server environment and replace IDs returned by earlier steps. ## Recipe: create and publish a grounded agent [#recipe-create-and-publish-a-grounded-agent] Create one API key with `sites:write`, `agents:write`, and `knowledge:write`. Add `sites:read` if the automation also lists or verifies sites. ### 1. Create the site [#1-create-the-site] ```bash curl --fail-with-body https://makeagent.fast/api/v1/sites \ -X POST \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-acme-site-2026-01" \ -d '{ "title": "Acme support", "slug": "acme-support", "type": "business", "template": "minimal", "content": { "headline": "Ask Acme support", "subheadline": "Answers grounded in our current policies.", "about": "Acme support answers product and account questions." }, "persona_mode": "assistant", "languages": ["en"] }' ``` A successful response is `201 Created`, includes the site under `data`, and returns its canonical resource path in the `Location` header. Save `data.id` as `SITE_ID`. ### 2. Configure the agent and embed policy [#2-configure-the-agent-and-embed-policy] ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites/$SITE_ID/agent" \ -X PATCH \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-acme-agent-2026-01" \ -d '{ "display_name": "Acme guide", "persona_mode": "assistant", "instructions": "Answer only from confirmed Acme sources. If the answer is missing, say that a teammate will follow up.", "languages": ["en"], "embed_enabled": true, "allowed_origins": ["https://www.acme.example", "https://staging.acme.example"] }' ``` Allowed origins are exact origins: scheme, hostname, and optional port. Do not include a path or trailing wildcard. ### 3. Add a text knowledge source [#3-add-a-text-knowledge-source] ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites/$SITE_ID/knowledge" \ -X POST \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-acme-returns-2026-01" \ -d '{ "name": "Returns policy 2026-01", "text": "Customers may request a return within 30 days of delivery. Contact support before sending an item back." }' ``` The public API currently ingests pasted text from 5 to 200,000 characters. Use the dashboard for URL crawling, uploads, YouTube, podcast, and feed sources. ### 4. Add a structured FAQ [#4-add-a-structured-faq] ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites/$SITE_ID/faqs" \ -X POST \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-acme-faq-returns-2026-01" \ -d '{ "question": "How long do I have to return an order?", "answer": "You may request a return within 30 days of delivery.", "approved": true }' ``` ### 5. Publish [#5-publish] ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites/$SITE_ID/publish" \ -X POST \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: launch-acme-publish-2026-01" \ -d '{"published": true}' ``` The response should contain `{"data":{"id":"...","status":"published"}}`. Open the hosted site and ask a question whose answer appears in the source. Publishing proves the site state changed; it does not prove the source quality or embed origin is correct. ## Recipe: paginate leads safely [#recipe-paginate-leads-safely] Use a key with `leads:read`. Treat `next_cursor` as opaque and URL-encode it. ```ts const base = `https://makeagent.fast/api/v1/sites/${siteId}/leads`; let cursor: string | null = null; do { const url = new URL(base); url.searchParams.set("limit", "100"); if (cursor) url.searchParams.set("cursor", cursor); const response = await fetch(url, { headers: { Authorization: `Bearer ${process.env.MAF_API_KEY}` }, }); if (!response.ok) throw await response.json(); const page = await response.json(); for (const lead of page.data) await exportLead(lead); cursor = page.next_cursor; } while (cursor); ``` Do not decode, edit, sort, or reuse a cursor for a different collection. ## Recipe: disable an embed during an incident [#recipe-disable-an-embed-during-an-incident] Use `agents:write` and keep the same idempotency key across network retries: ```bash curl --fail-with-body "https://makeagent.fast/api/v1/sites/$SITE_ID/agent" \ -X PATCH \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: incident-2026-07-embed-off" \ -d '{"embed_enabled": false}' ``` After the response succeeds, verify the launcher no longer initializes on an allowed host. Re-enable it with a new incident-resolution key after correcting the origin, content, or credential problem. ## Production checklist [#production-checklist] * Use a separate key per service and environment. * Persist resource IDs and idempotency keys before sending a mutation. * Set connect and response timeouts. * Honor `Retry-After` and cap retries. * Log operation name, HTTP status, and request ID without logging secrets or personal data. * Test against a non-production site before granting write scopes to production. --- # API reference Source: /docs/api/reference.md {/* docs-visuals */} The canonical machine-readable contract is [OpenAPI JSON](/api/openapi.json). This page is the human-readable operation index and explains rules shared by every endpoint. ## Base URL and version [#base-url-and-version] ```text https://makeagent.fast/api/v1 ``` The major version is part of the path. Additive fields can appear without a major-version change, so ignore unknown response fields. All management requests use HTTPS and a bearer API key or OAuth access token. ## Common headers [#common-headers] ```http Authorization: Bearer maf_live_... Accept: application/json Content-Type: application/json Idempotency-Key: your-stable-operation-key ``` `Content-Type` is required for `POST` and `PATCH` JSON bodies, including `{}`. `Idempotency-Key` is optional but strongly recommended for every `POST`, `PATCH`, and `DELETE`. ## Account and sites [#account-and-sites] | Method | Path | Scope | Purpose | | -------- | -------------------------- | -------------- | ----------------------------------------------------------------------- | | `GET` | `/me` | `account:read` | Read account, plan, entitlement, and wallet summary | | `GET` | `/sites` | `sites:read` | List owned sites | | `POST` | `/sites` | `sites:write` | Create a site and its default agent | | `GET` | `/sites/{site_id}` | `sites:read` | Retrieve one owned site | | `PATCH` | `/sites/{site_id}` | `sites:write` | Update safe site fields | | `DELETE` | `/sites/{site_id}` | `sites:write` | Delete the site and dependent resources | | `POST` | `/sites/{site_id}/publish` | `sites:write` | Publish or unpublish with `{ "published": boolean }` | | `GET` | `/sites/{site_id}/agent` | `agents:read` | Read agent settings | | `PATCH` | `/sites/{site_id}/agent` | `agents:write` | Update persona, instructions, voice, languages, embed state, or origins | Creating a site requires `title` and `content.headline`. Optional fields include `slug`, `type`, `template`, additional content, `persona_mode`, and one to four languages from `en`, `ko`, `uz`, and `ru`. ## Knowledge and FAQs [#knowledge-and-faqs] | Method | Path | Scope | Purpose | | -------- | ---------------------------------------- | ----------------- | -------------------------------------------------------------- | | `GET` | `/sites/{site_id}/knowledge` | `knowledge:read` | List knowledge sources | | `POST` | `/sites/{site_id}/knowledge` | `knowledge:write` | Ingest `{ "name", "text" }`; text must be 5–200,000 characters | | `DELETE` | `/sites/{site_id}/knowledge/{source_id}` | `knowledge:write` | Delete one source from future retrieval | | `GET` | `/sites/{site_id}/faqs` | `knowledge:read` | List structured FAQs | | `POST` | `/sites/{site_id}/faqs` | `knowledge:write` | Create `{ "question", "answer", "approved" }` | | `PATCH` | `/sites/{site_id}/faqs/{faq_id}` | `knowledge:write` | Update question, answer, or approval state | | `DELETE` | `/sites/{site_id}/faqs/{faq_id}` | `knowledge:write` | Delete an FAQ | The public API ingests pasted text. Use the dashboard for URL crawling, file upload, YouTube, podcast, and feed workflows. ## Conversations, leads, analytics, and usage [#conversations-leads-analytics-and-usage] | Method | Path | Scope | Purpose | | ------ | -------------------------------------------------- | -------------------- | ---------------------------------------- | | `GET` | `/sites/{site_id}/conversations` | `conversations:read` | List conversation threads | | `GET` | `/sites/{site_id}/conversations/{conversation_id}` | `conversations:read` | Retrieve a thread and paginated messages | | `GET` | `/sites/{site_id}/leads` | `leads:read` | List captured leads | | `GET` | `/sites/{site_id}/analytics` | `analytics:read` | List analytics events | | `GET` | `/sites/{site_id}/usage` | `usage:read` | List metered usage events | These event and activity collections accept `limit` and `cursor`. Returned records can contain personal data; store and export them according to your privacy obligations. Conversation and lead resources are read-only in v1. Sending visitor messages, posting owner replies, and creating leads are not public API operations. ## Connectors and domains [#connectors-and-domains] | Method | Path | Scope | Purpose | | -------- | --------------------------------------------- | ------------------ | --------------------------------------------------------------- | | `GET` | `/sites/{site_id}/connectors` | `connectors:read` | List connector state without secrets | | `POST` | `/sites/{site_id}/connectors` | `connectors:write` | Create or replace a channel connector and provision its webhook | | `PATCH` | `/sites/{site_id}/connectors/{connector_id}` | `connectors:write` | Set `status` or `reply_with_voice` | | `DELETE` | `/sites/{site_id}/connectors/{connector_id}` | `connectors:write` | Deprovision and remove a connector | | `GET` | `/sites/{site_id}/domains` | `domains:read` | Read custom-domain state and DNS requirements | | `POST` | `/sites/{site_id}/domains` | `domains:write` | Connect `{ "domain", "primary_host" }` | | `PATCH` | `/sites/{site_id}/domains/{domain_id}` | `domains:write` | Change `primary_host` between `apex` and `www` | | `POST` | `/sites/{site_id}/domains/{domain_id}/verify` | `domains:write` | Check ownership DNS or refresh provider activation | | `DELETE` | `/sites/{site_id}/domains/{domain_id}` | `domains:write` | Detach the domain and cancel associated add-on billing | Connector creation uses a channel-discriminated body. Credential fields differ for `telegram`, `whatsapp`, `messenger`, `discord`, and `kakao`; use the matching connector guide before sending secrets. ## Broadcasts, notifications, and monetization [#broadcasts-notifications-and-monetization] | Method | Path | Scope | Purpose | | -------- | ----------------------------------------------------- | --------------------- | ------------------------------------------------------- | | `GET` | `/sites/{site_id}/broadcasts` | `broadcasts:read` | List broadcasts | | `POST` | `/sites/{site_id}/broadcasts` | `broadcasts:write` | Create a Kakao broadcast draft | | `POST` | `/sites/{site_id}/broadcasts/{broadcast_id}/send` | `broadcasts:write` | Queue an existing draft; returns `202` | | `GET` | `/sites/{site_id}/notifications` | `notifications:read` | List owner notifications | | `PATCH` | `/sites/{site_id}/notifications/{notification_id}` | `notifications:write` | Mark read or unread with `{ "read": boolean }` | | `GET` | `/sites/{site_id}/monetization/products` | `monetization:read` | List site products | | `POST` | `/sites/{site_id}/monetization/products` | `monetization:write` | Create an access, consultation, tip, or digital product | | `PATCH` | `/sites/{site_id}/monetization/products/{product_id}` | `monetization:write` | Update product fields and active state | | `DELETE` | `/sites/{site_id}/monetization/products/{product_id}` | `monetization:write` | Delete a product | Broadcast creation currently accepts only the `kakao` channel. Queueing fails with `409 conflict` when there are no reachable recipients or the broadcast is no longer a draft. ## Developer webhooks [#developer-webhooks] | Method | Path | Scope | Purpose | | -------- | ---------------------------------- | ---------------- | -------------------------------------------------------------- | | `GET` | `/webhook-endpoints` | `webhooks:read` | List endpoint state without signing secrets | | `POST` | `/webhook-endpoints` | `webhooks:write` | Register a public HTTPS URL and reveal its signing secret once | | `DELETE` | `/webhook-endpoints/{endpoint_id}` | `webhooks:write` | Delete an endpoint and stop future delivery | Webhook URLs must use HTTPS, cannot include credentials or a custom port, and must resolve only to public IP addresses. ## Pagination [#pagination] Paginated collections accept `limit` from `1` to `100`; the default is `50`. Use the returned opaque `next_cursor` only with the same collection and filters. ```json { "data": [], "has_more": false, "next_cursor": null } ``` Some small configuration collections return the same envelope with `has_more: false` and do not need a cursor. ## Idempotency [#idempotency] An idempotency key must contain 8–200 characters from letters, numbers, `.`, `_`, `:`, and `-`. Results are retained for 24 hours and scoped to the credential owner. * Same key and same method, path, query, and body: returns the stored response with `x-idempotent-replayed: true`. * Same key with different input: returns `409 conflict`. * Same key while the first request is processing: returns `409 conflict` and `Retry-After: 2`. Persist the key before sending the mutation and reuse it only for retries of that logical operation. ## Responses and errors [#responses-and-errors] Success responses wrap their resource in `data`. Create operations normally return `201`; queued broadcast sends return `202`; other successful operations return `200`. Every response includes `x-request-id` and uses `Cache-Control: no-store` for API data. See [Scopes and errors](/docs/api/scopes-errors) for the complete error-code table and [API recipes](/docs/api/recipes) for copyable workflows. --- # Scopes and errors Source: /docs/api/scopes-errors.md {/* docs-visuals */} Every public API operation requires one explicit scope. A valid credential without that scope receives `403 insufficient_scope`; it is never silently upgraded to broader access. ## Scope reference [#scope-reference] | Scope | Allows | | --------------------- | -------------------------------------------------------------------------------- | | `account:read` | Read the authenticated account, plan, entitlements, and wallet summary | | `sites:read` | List and retrieve sites | | `sites:write` | Create, update, publish, and unpublish sites | | `agents:read` | Read agent settings | | `agents:write` | Update persona, instructions, languages, voice, embed state, and allowed origins | | `knowledge:read` | List knowledge sources and FAQs | | `knowledge:write` | Ingest or delete text sources and create, update, or delete FAQs | | `conversations:read` | List conversations and retrieve their messages | | `leads:read` | List captured leads | | `analytics:read` | Read site analytics events | | `connectors:read` | List connector state without returning secrets | | `connectors:write` | Create, update, enable, disable, and remove connectors | | `domains:read` | Read domain state and required DNS records | | `domains:write` | Connect, verify, update, and remove domains | | `broadcasts:read` | List broadcasts and delivery state | | `broadcasts:write` | Create drafts and queue a broadcast send | | `notifications:read` | List owner notifications | | `notifications:write` | Mark notifications read or unread | | `monetization:read` | List monetization products | | `monetization:write` | Create, update, activate, deactivate, and delete products | | `usage:read` | Read metered usage events | | `webhooks:read` | List webhook endpoints and delivery state | | `webhooks:write` | Create and delete webhook endpoints | Use read scopes for reporting jobs. Add a write scope only when the integration performs that mutation. For example, a lead export normally needs `sites:read` and `leads:read`, not `sites:write` or `agents:write`. ## Scope failures [#scope-failures] ```json { "error": { "code": "insufficient_scope", "message": "The API key does not grant the required scope.", "details": { "required": ["sites:write"] }, "request_id": "6c0b2f2e-..." } } ``` Create a replacement key with the missing scope and update the server secret. Existing API-key scopes cannot be expanded in place; this makes privilege changes explicit and auditable. ## Error envelope [#error-envelope] All API errors use the same top-level shape: ```ts type ApiError = { error: { code: string; message: string; details?: unknown; request_id: string; }; }; ``` Branch on `error.code`, not the English `message`. Messages can improve without a version change. Log `error.request_id` and the `x-request-id` response header with the operation name, status, and retry attempt—but never log the authorization header or request secrets. ## Status and code reference [#status-and-code-reference] | HTTP | Stable code | Meaning | Normal response | | ----- | ------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------- | | `400` | `invalid_request` | Validation failed or a field is unsupported | Correct the fields in `details`; do not retry unchanged input | | `400` | `invalid_json` | Body is not a JSON object or contains invalid JSON | Serialize one valid JSON object | | `401` | `authentication_required` | Bearer header is missing | Attach a server-side credential | | `401` | `invalid_api_key` | Key is invalid, expired, revoked, or its owner no longer exists | Replace or rotate the key | | `403` | `subscription_required` | The owner does not have an active paid plan | Restore the subscription before retrying | | `403` | `insufficient_scope` | The credential lacks the operation's scope | Create a least-privilege replacement credential | | `403` | `account_paused` | API access is paused for the account | Resume API access or contact the account owner | | `404` | `not_found` | Endpoint or owned resource was not found | Verify the path and tenant-owned ID | | `409` | `conflict` | Current resource state prevents the action | Read current state before retrying | | `415` | `invalid_request` | Mutation body is not `application/json` | Send `Content-Type: application/json` | | `429` | `rate_limit_exceeded` | The key's one-minute plan bucket is exhausted | Wait for `Retry-After` and apply jitter | | `500` | `internal_error` | The request could not be completed | Retry a safe or idempotent operation and report the request ID | Resources outside the credential owner's account can return `404` instead of revealing whether another tenant's ID exists. ## Validation details [#validation-details] Field validation returns paths that can be shown next to your own form controls: ```json { "error": { "code": "invalid_request", "message": "Request validation failed.", "details": [ { "path": "content.headline", "message": "String must contain at least 1 character(s)" } ], "request_id": "6c0b2f2e-..." } } ``` Mutation bodies must be JSON objects. Arrays, empty bodies, form data, and JSON sent with a text content type are rejected. ## Retry decisions [#retry-decisions] * Retry `429` after the number of seconds in `Retry-After`. * Retry `500` only for safe reads or writes protected by the same `Idempotency-Key`. * Retry the in-progress idempotency `409` after its `Retry-After: 2` header. * Do not retry `400`, `401`, `403`, `404`, or a state conflict until the cause changes. See [Rate limits](/docs/api/rate-limits) for backoff behavior and [API reference](/docs/api/reference) for the required scope on each operation. --- # SDK status Source: /docs/api/sdks.md {/* docs-visuals */} The versioned REST API is the supported public integration surface today. JavaScript, Python, and framework client source exists in the product repository, but the packages are not currently published to npm or PyPI. ## Availability [#availability] | Client | Status | How to use it | | ------------------------------ | -------------------------------- | ------------------------------------------------------------------------------------------- | | REST over HTTPS | **Available** | Call `https://makeagent.fast/api/v1` from any server HTTP client | | OpenAPI document | **Available** | Download [/api/openapi.json](/api/openapi.json) and inspect it before generating a client | | `@make-agent-fast/sdk` | **Source preview — not on npm** | Do not add this import to a production project until a registry release is linked here | | `makeagentfast` Python package | **Source preview — not on PyPI** | Use `requests`, `httpx`, or the Python standard library until a PyPI release is linked here | | React, Vue, Svelte adapters | **Source preview — not on npm** | Prefer the documented script-loader pattern for the framework | | Web component | **Source preview — not on npm** | Prefer the standard `embed.js` loader | Do not run `npm install @make-agent-fast/sdk` or `pip install makeagentfast` based only on an import example. This page will include the registry URL, version, integrity guidance, and installation command when a public package is released. ## JavaScript without an SDK [#javascript-without-an-sdk] ```ts const response = await fetch("https://makeagent.fast/api/v1/sites?limit=100", { headers: { Authorization: `Bearer ${process.env.MAF_API_KEY}`, Accept: "application/json", }, }); const body = await response.json(); if (!response.ok) { console.error(body.error.code, body.error.request_id); throw new Error(`Make Agent Fast request failed (${response.status})`); } for (const site of body.data) console.log(site.id, site.title); ``` Keep the API key on the server. A React, Vue, or Svelte browser bundle should call your own backend or install the public embed loader; it should not call the management API with a secret. ## Python without an SDK [#python-without-an-sdk] ```python import json import os import urllib.request request = urllib.request.Request( "https://makeagent.fast/api/v1/sites?limit=100", headers={ "Authorization": f"Bearer {os.environ['MAF_API_KEY']}", "Accept": "application/json", }, ) with urllib.request.urlopen(request, timeout=20) as response: payload = json.load(response) for site in payload["data"]: print(site["id"], site["title"]) ``` Catch `urllib.error.HTTPError` in production and parse its JSON body using the [stable error envelope](/docs/api/scopes-errors). ## Generated clients [#generated-clients] OpenAPI generation is useful only when the document contains the operations and schemas your application depends on. Pin the downloaded specification, generate into a reviewable directory, and inspect every diff before upgrading. ```bash curl --fail-with-body https://makeagent.fast/api/openapi.json \ --output make-agent-fast.openapi.json ``` Do not automatically regenerate and deploy a client from the live URL. A new additive field should not break a client, but generator behavior and generated method names can still change. ## What a public SDK release must include [#what-a-public-sdk-release-must-include] Before any preview client is marked available, it must have: * A public registry page and immutable semantic version. * Installation, upgrade, and supported-runtime instructions. * Typed request and response models generated from the complete API contract. * Structured errors that retain the HTTP status and request ID. * Pagination helpers that treat cursors as opaque. * Explicit idempotency-key support for mutations. * CI tests against the current `/api/v1` contract and a published changelog. Until those conditions are met, use the REST examples in [API quickstart](/docs/api/quickstart) and [API recipes](/docs/api/recipes). --- # Webhooks Source: /docs/api/webhooks.md {/* docs-visuals */} Developer webhooks send account events to your server so you do not need to poll. Delivery is at least once: events can be duplicated, delayed, or arrive out of order. ## Register an endpoint [#register-an-endpoint] Create a key with `webhooks:write`, then register a public HTTPS receiver and the smallest event set you need. ```bash curl --fail-with-body https://makeagent.fast/api/v1/webhook-endpoints \ -X POST \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: webhook-production-v1" \ -d '{ "url": "https://api.example.com/webhooks/make-agent-fast", "events": ["lead.created", "conversation.started"] }' ``` The URL must use HTTPS, cannot contain a username, password, fragment, or custom port, and must resolve only to public IP addresses. Redirects are not followed during delivery. The `201` response reveals a `whsec_...` signing secret once. Store it in the receiver's secret manager before closing the response. Listing endpoints later returns URL, events, status, last delivery, and last error—but not the secret. ## Event types [#event-types] | Event | Emitted when | | ------------------------------ | --------------------------------------- | | `site.published` | A site becomes published | | `site.unpublished` | A site is returned to draft state | | `lead.created` | The agent captures a new lead | | `conversation.started` | A new conversation thread begins | | `conversation.message.created` | A message is added to a conversation | | `knowledge.ready` | A knowledge source completes processing | | `broadcast.sent` | A broadcast finishes its send workflow | | `domain.activated` | A custom domain becomes active | ## Request format [#request-format] Make Agent Fast sends `POST` with `Content-Type: application/json`, `User-Agent: MakeAgentFast-Webhooks/1.0`, and these headers: ```http maf-event-id: evt_... maf-event-type: lead.created maf-signature: t=1784210566,v1=HEX_HMAC_SHA256 ``` The JSON body has a stable envelope. Fields inside `data` depend on the event and can receive additive fields. ```json { "id": "evt_2c34...", "type": "lead.created", "created_at": "2026-07-16T10:02:46.000Z", "data": { "site_id": "SITE_ID", "lead_id": "LEAD_ID" } } ``` Use the body `id` or `maf-event-id` as the deduplication key. Do not use delivery time or a generated database ID. ## Verify the signature in Node.js [#verify-the-signature-in-nodejs] Read the exact raw bytes before JSON parsing. The signed value is `TIMESTAMP + "." + RAW_BODY`. ```ts import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyWebhook(rawBody: Buffer, header: string, secret: string) { const values = Object.fromEntries(header.split(",").map((part) => part.split("=", 2))); const timestamp = values.t; const supplied = values.v1; if (!timestamp || !supplied || !/^[a-f0-9]{64}$/.test(supplied)) return false; const age = Math.abs(Date.now() / 1_000 - Number(timestamp)); if (!Number.isFinite(age) || age > 300) return false; const expected = createHmac("sha256", secret) .update(`${timestamp}.`) .update(rawBody) .digest("hex"); return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(supplied, "hex")); } ``` Reject malformed signatures and timestamps older than your accepted replay window; five minutes is a reasonable default. Compare fixed-length bytes in constant time. ## Verify the signature in Python [#verify-the-signature-in-python] ```python import hashlib import hmac import time def verify_webhook(raw_body: bytes, header: str, secret: str) -> bool: values = dict(part.split("=", 1) for part in header.split(",") if "=" in part) timestamp = values.get("t", "") supplied = values.get("v1", "") try: if abs(time.time() - int(timestamp)) > 300: return False except ValueError: return False signed = timestamp.encode() + b"." + raw_body expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, supplied) ``` Only parse and process the JSON after signature verification succeeds. ## Acknowledge and process [#acknowledge-and-process] 1. Verify the signature and timestamp. 2. Insert the event ID into a table with a unique constraint. 3. If it already exists, return `204` without repeating the side effect. 4. Commit the event or enqueue your internal job. 5. Return a `2xx` response quickly. The Make Agent Fast sender times out after 10 seconds. It considers only `2xx` successful and does not follow redirects. ## Retry behavior [#retry-behavior] Failed deliveries are queued for up to eight attempts. Backoff starts around two seconds, doubles with jitter, and is capped at one hour; the actual delivery time can be later when workers are busy. Because retries can outlive the initiating request and arrive out of order, never depend on one event arriving immediately before another. Return a non-2xx response only when you want Make Agent Fast to retry. For a permanently unsupported event version or deleted destination, accept and record it or remove the endpoint instead of producing endless transient failures. ## Rotate a signing secret [#rotate-a-signing-secret] Signing secrets cannot be revealed or edited. To rotate safely: 1. Create a second endpoint pointing to a temporary or versioned receiver path. 2. Store its newly revealed secret. 3. Accept and deduplicate events from both endpoints. 4. Verify the new endpoint receives valid deliveries. 5. Delete the old endpoint with `DELETE /webhook-endpoints/{endpoint_id}`. If the receiver URL remains the same, make the receiving application accept both secrets during the overlap and use event IDs to prevent duplicate side effects. ## Troubleshooting [#troubleshooting] | Symptom | Check | | --------------------- | -------------------------------------------------------------------------------------------------------------- | | No deliveries | Endpoint status, selected events, public DNS, HTTPS certificate, and whether the event actually occurred | | Signature mismatch | Raw body access, exact `timestamp.body` concatenation, correct endpoint secret, and proxy body transformations | | Repeated deliveries | Return code, 10-second timeout, internal exceptions, and event-ID uniqueness | | Private-address error | DNS records must resolve only to public addresses; localhost and internal ranges are rejected | | Redirect failure | Register the final HTTPS URL directly; redirects are not followed | Never include the signing secret, bearer key, or full visitor payload in a support message. Include the endpoint ID, event ID, failure time, and sanitized receiver logs. --- # Appearance Source: /docs/build/appearance.md {/* docs-visuals */} ## Start with structure [#start-with-structure] Choose a look that fits the content density and audience. Confirm section order and calls to action before fine-tuning colors or type. ## Brand controls [#brand-controls] Set imagery, colors, typography, spacing, and agent presentation from the appearance controls available to your site. Use sufficient contrast and keep interactive states distinguishable. ## Responsive review [#responsive-review] Preview at desktop and narrow mobile widths. Check long titles, Korean text, navigation, forms, chat controls, and the agent launcher. ## Accessibility [#accessibility] Provide meaningful link labels, avoid text embedded only in images, keep body copy readable, and do not rely on color alone to communicate status. ## Branding badge [#branding-badge] Badge removal is available on Pro and Studio. Starter sites retain the Make Agent Fast referral badge. --- # Create an agent Source: /docs/build/create-agent.md {/* docs-visuals */} ## Choose a starting method [#choose-a-starting-method] Use a prompt when you already know the outcome and source material. Use a template when you want a proven structure for a portfolio, service, product, or other common site. ## Write a useful brief [#write-a-useful-brief] Include the subject, audience, primary action, required sections, and facts that must be preserved. Do not paste passwords, private customer data, or connector tokens into a generation prompt. ```text Create a concise product-support site for Acme Camera. Audience: new customers. Primary action: help users choose a model or contact sales. Include: model comparison, warranty, shipping, and contact details. Tone: informed and calm. ``` ## Review generated content [#review-generated-content] Generation is a draft, not a source of truth. Verify names, prices, claims, contact details, links, and legal language before publishing. Then move durable facts into knowledge sources and durable behavior into agent instructions. ## Iterate in the visual builder [#iterate-in-the-visual-builder] Use **Chat to build** for natural-language changes such as adding a section, changing the tone, or reorganizing the page. Each successful AI edit updates the live preview and saved site content; **Undo last edit** restores the preceding saved version. The composer also accepts visual and spoken input: * Attach, paste, or drop up to 4 JPG, PNG, WebP, HEIC, or HEIF reference images. * Keep each image under 5 MB and all attached images under 12 MB. * Use **Speak to type** for a recording of up to one minute. The browser needs microphone permission and the configured transcription provider must be available. * Image analysis and speech transcription call live providers and can consume credits. A visual reference guides the edit; it is not automatically published as a site asset. Use **Fine-tune** when you need deterministic edits instead of another AI pass. Change the site name, look, section structure, links, and contact details in the left panel, and edit highlighted copy directly in the preview. Fine-tune changes are not final until you select **Save**; the page warns before navigation when unsaved changes remain. --- # FAQs and products Source: /docs/build/faqs-products.md {/* docs-visuals */} ## Use FAQs for canonical answers [#use-faqs-for-canonical-answers] Create one FAQ per visitor intent. Write the question in language customers use and keep the answer complete without relying on another FAQ. ## Use products for catalog facts [#use-products-for-catalog-facts] Products are appropriate for a named offer with fields such as description, price, availability, or link. Keep transactional facts current and avoid promising inventory or delivery unless your source is updated reliably. ## Avoid duplication [#avoid-duplication] Do not maintain the same price in a note, FAQ, page section, and product record. Choose one canonical source and link or refer to it elsewhere. ## Verify customer-facing behavior [#verify-customer-facing-behavior] After a change, ask the agent the FAQ in multiple phrasings and confirm the product link and price are correct. --- # Knowledge sources Source: /docs/build/knowledge.md {/* docs-visuals */} Knowledge gives the agent retrievable source material. Adding a source extracts plain text, divides it into passages, creates embeddings, and associates those passages with the current site. At answer time, the agent retrieves relevant passages instead of receiving every source in every prompt. ## Choose the right source type [#choose-the-right-source-type] | Source | Best for | Important behavior | | ----------- | ----------------------------------------------- | ------------------------------------------------------------------ | | **Paste** | Policies, contact details, short internal notes | 5–200,000 characters; no automatic refresh | | **URL** | Public pages maintained elsewhere | One page by default; optionally crawl 5, 10, or 20 same-site pages | | **Upload** | PDF, `.txt`, `.md`, `.markdown`, `.csv` | Maximum 20 MB; scanned image-only PDFs have no selectable text | | **YouTube** | Public videos with useful spoken content | Tries captions first, then available transcription; best effort | | **Podcast** | A public episode audio URL or supported feed | Requires fetchable audio and an available transcription provider | | **Feed** | Public RSS/Atom blog or podcast updates | Extracts available feed items as text; best effort | Use **FAQ and products** for predictable question/answer or catalog fields rather than burying them in a long document. See [FAQs and products](/docs/build/faqs-products). ## Add a source [#add-a-source] 1. Open the site's **Agent** page and find **Knowledge**. 2. Select **Paste**, **URL**, **Upload**, **YouTube**, **Podcast**, or **Feed**. 3. Enter content or choose a file. 4. For a normal URL, choose the number of pages to crawl. For fetched sources, optionally choose **Daily**, **Weekly**, or **Never** auto-refresh. 5. Select **Add** and wait for the result showing the number of indexed chunks. 6. Ask a question whose answer appears only in the new source. Knowledge ingestion consumes credits because source passages are embedded. It is rejected when the workspace has no active access or usable credit balance. ## Crawl public pages safely [#crawl-public-pages-safely] The crawler accepts public HTTP/HTTPS content and blocks unsafe private-network destinations. Start with one canonical page. Increase the page count only when the linked pages are part of the same useful information set; a larger crawl can add navigation, legal boilerplate, duplicated text, or stale archives that weaken retrieval. The selected count is a maximum, not a promise. Robots behavior, authentication, client-only rendering, redirects, content type, response size, and link structure can reduce the number fetched. A page response is capped at 5 MB and must contain enough extractable text. For important facts behind login, paste an approved summary or upload a sanitized export instead of attempting to crawl an authenticated page. ## Use files and media [#use-files-and-media] Uploaded PDF and text-like files must contain extractable text. OCR is not performed for a scanned image-only PDF; convert it to searchable text before upload. Keep tables simple because layout-heavy documents flatten into reading order during extraction. YouTube ingestion prefers an available caption track. If captions are unavailable, transcription is provider- and network-dependent. Podcast audio must be publicly fetchable. Feed results depend on how much article or episode text the feed exposes. After any media import, inspect the source title/chunk count and test a specific fact instead of assuming the full recording was captured. ## Plan refresh and replacement [#plan-refresh-and-replacement] Daily and weekly schedules are available for URL, YouTube, podcast, and feed sources. The system re-fetches the saved source origin and replaces its retrievable passages when a scheduled crawl succeeds. The source list shows the refresh cadence and last refreshed time. Paste and upload sources are snapshots. To correct or replace one, delete it and add the updated content. Deleting a source removes its passages from future retrieval; it does not edit historical messages that already quoted or summarized the old version. Use one clear owner for pricing, policies, and schedules. If two active sources disagree, retrieval can surface either statement. Remove superseded versions rather than adding “new” copies beside them. ## Write content the agent can retrieve [#write-content-the-agent-can-retrieve] * State the subject in each section: “Pro plan cancellation policy” is more retrievable than “More details.” * Put the answer in plain language near the relevant heading. * Include units, currency, timezone, effective date, and exceptions where they matter. * Use stable canonical URLs and descriptive file names. * Separate public information from internal instructions or private records. * Keep persona behavior in [Persona and instructions](/docs/build/persona), not inside factual source material. ## Test grounded answers [#test-grounded-answers] Create a small acceptance set before publishing: 1. A direct question whose answer is explicit in the source. 2. A paraphrase that uses different words. 3. A boundary question the source does not answer. 4. A recently changed fact. 5. A language variant for each configured agent language. The correct result is not always a confident answer. When the source lacks the fact, the agent should follow its fallback/escalation instructions rather than inventing one. Check **Conversations** after launch for repeated unanswered questions and turn validated answers into a source or structured FAQ. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------ | ------------------------------------------------------------------------------------------- | | URL reports no usable text | Public access, supported content type, server-rendered text, redirect target, and page size | | PDF reports no selectable text | Whether it is a scan; OCR/export it to a searchable PDF or text file | | YouTube/podcast import fails | Public URL, captions/audio availability, transcription provider key, and fetch restrictions | | Correct fact is ignored | Processing result, wording, conflicting sources, source age, and the exact test question | | Agent reveals too much | Remove the private source immediately and tighten persona boundaries and account access | | Old answer persists | Delete/replace the stale source, wait for re-indexing, and start a new test conversation | Never upload credentials, private keys, unrestricted customer exports, health records, or any content the public agent must never reveal. --- # Persona and instructions Source: /docs/build/persona.md {/* docs-visuals */} ## Separate identity from knowledge [#separate-identity-from-knowledge] Persona explains who the agent is and how it behaves. Knowledge supplies facts. Put “Speak as the Acme support assistant” in persona; put the current warranty period in a knowledge source. ## Instruction order [#instruction-order] Write instructions in this order: 1. Identity and role 2. Primary visitor goal 3. Tone and response length 4. Required actions and questions 5. Prohibited claims and sensitive topics 6. Escalation and lead-capture conditions ## Boundaries [#boundaries] Tell the agent when to say it does not know, when to cite or qualify an answer, and when to offer human help. Never instruct it to hide uncertainty or invent missing data. ## Test changes [#test-changes] Persona changes can affect every channel. Re-test common questions, refusals, lead capture, and supported languages before publishing a major change. --- # Voice and live calls Source: /docs/build/voice.md {/* docs-visuals */} ## Understand the three interaction modes [#understand-the-three-interaction-modes] | Mode | Visitor experience | Entitlement and usage | | ----------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | **Text chat** | Types a message and receives text | Always enabled; consumes normal AI credits | | **Voice messages** | Records a message, waits for transcription/agent/TTS, receives a reply | Included on all plans; transcription, model, and speech work consume credits | | **Live real-time call** | Holds a low-latency spoken conversation | Pro: 30 min/month; Studio: 90 min/month; Starter: unavailable; also charges credits up front for the configured call limit | Text cannot be disabled. Voice-message and real-time toggles control which additional surfaces visitors may use. A saved real-time toggle does not override the workspace's plan entitlement or missing provider configuration. ## Configure interaction behavior [#configure-interaction-behavior] 1. Open the site's **Agent** page and find **Voice**. 2. Keep **Text chat** on and choose **Voice messages** and/or **Live real-time voice**. 3. If the agent has multiple languages, optionally enable live language mirroring. 4. Set the per-call automatic limit. 5. Select a stock or entitled cloned voice. 6. Save and test each enabled mode separately. The call limit accepts 30 seconds through 20 minutes in 30-second steps; the default is 3 minutes. It ends an individual call and protects the monthly allowance, but it does not increase the plan's remaining live minutes. ## Choose languages and a voice [#choose-languages-and-a-voice] Agents can be configured for English, Korean, Uzbek, and Russian. The first language is primary. A single-language agent keeps answering in that language. A multilingual text agent mirrors the visitor when the visitor uses one of the configured languages. For live calls, language mirroring is an explicit option shown only with two or more languages. When it is off, live calls use the primary language. Uzbek voice is currently best effort because provider speech coverage is less complete; test it with your exact provider and audience before launch. Stock voices include English-first, multilingual, and Korean-native options. The language label describes the curated delivery, not a hard guarantee. Preview the chosen voice when the dashboard preview control is available, and always test names, abbreviations, currencies, phone numbers, and specialized terminology in the real visitor flow. ## Provider keys and fallback [#provider-keys-and-fallback] Voice requires more than a model key: * Speech-to-text uses a configured transcription provider such as Deepgram or OpenAI. * Voice messages and connector audio replies need a text-to-speech provider such as ElevenLabs or OpenAI. * Real-time calls need the configured real-time provider and a plan allowance. * Voice cloning requires ElevenLabs cloning availability plus a Pro or Studio entitlement. Configure keys under **Settings → AI provider keys** and follow [Provider keys](/docs/account/provider-keys). A successful text chat proves only the model path; it does not prove transcription, speech synthesis, media upload, or real-time session minting. ## Test voice messages [#test-voice-messages] Use the public hosted site or real embed, not only an owner preview. Grant microphone access, record a short question, and confirm the transcript meaning plus spoken/text response. Then test: * quiet and noisy environments; * mobile Safari and Chrome where relevant; * a long question near the 25 MB public audio cap; * denied microphone permission; * unsupported or low-quality audio; * each configured language. For embedded sites, the parent CSP and Permissions Policy must allow microphone use. See [Security and CSP](/docs/embed/security). ## Test live calls [#test-live-calls] Confirm the plan has unused live minutes, the site is accessible, and the real-time provider key validates. Begin with a short call, interrupt the agent, switch languages only when mirroring is enabled, and allow the configured call limit to end one test session. Live minutes are a separate monthly entitlement from normal credits. Issuing the live token charges credits for the server-authorized maximum duration and reserves that duration against the monthly live allowance. Because media flows directly between the browser and provider, a shorter client-reported call cannot be verified and unused time is not partially refunded after token delivery. Server-side token-mint failures are released; abandoned sessions are charged at their authorized limit. End tests cleanly and use the configured call limit when forecasting. See [Credits and usage](/docs/operate/usage). ## Clone a voice responsibly [#clone-a-voice-responsibly] Voice cloning is unavailable on Starter. Pro includes one clone; Studio allows multiple under fair use. Re-cloning a site that already has a clone replaces that site's clone rather than consuming another slot. Use only your own voice or a voice for which you have explicit, documented authorization. The cloning form requires consent and may require typing a site-scoped challenge phrase. A successful clone creates an auditable consent record when the stronger consent gate is enabled. Upload at least one and at most 10 audio samples. Each file may be at most 10 MB, with a 50 MB combined maximum. Use clean, dry recordings with one speaker, consistent distance, minimal background noise, and representative pronunciation. Do not mix different speakers or heavily processed music. Cloning reserves and then settles credits. A failed provider call refunds the reservation; a successful clone becomes the agent's selected voice. ## Disclosure and safety [#disclosure-and-safety] Tell callers they are interacting with AI and synthetic or cloned speech when required by applicable law, provider policy, or user expectations. Do not clone or imitate a person without authorization, use a clone for deceptive identity claims, or collect unnecessary recordings. Define how recordings, transcripts, consent records, and downstream provider data are retained and deleted. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ---------------------------------- | ------------------------------------------------------------------------------------------------------ | | Text works but voice controls fail | Transcription/TTS/realtime provider keys, feature toggle, plan, credit balance, and browser permission | | Recording cannot start | HTTPS, microphone permission, device input, embed Permissions Policy, and browser support | | Transcript is inaccurate | Audio quality, chosen language set, accent/terminology, and transcription-provider coverage | | Spoken reply sounds wrong | Selected voice, source text, punctuation, language label, and provider availability | | Live call ends immediately | Remaining minutes, per-call limit, provider key, network, and concurrent session state | | Clone is rejected | Plan slot, consent phrase, 1–10 supported audio files, per-file/combined limits, and ElevenLabs key | Treat voice as a separate launch surface: text acceptance tests are necessary but not sufficient. --- # Discord Source: /docs/connectors/discord.md {/* docs-visuals */} ## Create the Discord application [#create-the-discord-application] Follow Discord's official [app quick start](https://docs.discord.com/developers/quick-start/getting-started) and [interactions overview](https://docs.discord.com/developers/interactions/overview). Create an application in the Discord Developer Portal and collect: | Make Agent Fast field | Discord location | Purpose | | --------------------- | ------------------- | ----------------------------------------------------------------------- | | **Application ID** | General Information | Identifies the application and command | | **Public Key** | General Information | Verifies Ed25519-signed interactions; exactly 64 hexadecimal characters | | **Bot token** | Bot page | Validates the application and registers `/ask`; keep secret | Resetting a bot token invalidates the previous one immediately. The Public Key is not secret, but it must belong to the same application as the bot token. ## Connect and register `/ask` [#connect-and-register-ask] Open the site's **Connectors** page, choose **Discord**, enter the three values, and select **Connect**. Make Agent Fast validates the bot token against Discord's application endpoint and rejects a Public Key that does not match the application. It normalizes a mistyped Application ID to the authenticated application's real ID, then creates or updates the global `/ask question` command without deleting other application commands. After saving, copy the displayed **Interactions Endpoint URL** into **General Information → Interactions Endpoint URL** in Discord and save. Discord sends a signed `PING`; Make Agent Fast validates the signature and returns the required `PONG`. ## Install and test [#install-and-test] Use the Discord installation settings to authorize the app in a test server with the permissions required for application commands. Global command propagation can take time. Run `/ask`, fill the required `question` option, and submit. Make Agent Fast acknowledges the interaction before the short Discord deadline, runs the agent, then edits the deferred original response. Conversation history is scoped per Discord user so later `/ask` requests can retain context. The current connector is text-only. It does not read voice channels, attachments, ordinary channel messages, or direct messages; only the registered `/ask` interaction enters the agent. ## Rotate or disconnect [#rotate-or-disconnect] If the bot token is exposed, reset it in Discord, update the connector, and verify `/ask` again. If the application itself changes, update all three values and the Interactions Endpoint URL for that application. Disabling or removing the connector does not delete Discord's application or global command. Remove the app from servers and delete obsolete provider credentials separately when decommissioning it. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | Connector rejects the fields | Use the bot token and 64-character Public Key from the same application | | Discord rejects the endpoint | Confirm public HTTPS, paste the generated endpoint, and re-save credentials before retrying | | `/ask` is missing | Confirm command installation/authorization, wait for global propagation, and reconnect to register again | | Interaction says the app did not respond | Check the connector is enabled and inspect Make Agent Fast/Discord errors; endpoint signature validation must succeed | | Other chat messages are ignored | Expected; the integration handles `/ask` only | | Reply is truncated | Discord message responses are limited to 2,000 characters | Never expose the bot token in client code, repositories, logs, or screenshots. --- # KakaoTalk Source: /docs/connectors/kakao.md {/* docs-visuals */} ## Prepare Kakao assets [#prepare-kakao-assets] Create or select the Kakao Developers application, KakaoTalk Channel, and Kakao i Open Builder bot that will own the integration. Review Kakao's official [Open Builder documentation](https://i.kakao.com/docs/key-concepts-block#skill) for skills and blocks. Collect or create these values: | Make Agent Fast field | Source | Purpose | | --------------------- | ---------------------------------- | ------------------------------------------------------------ | | **Channel ID** | KakaoTalk Channel public/search ID | Associates the connector with the intended channel | | **REST API key** | Kakao Developers app keys | Validates the Kakao application | | **Bot secret** | A long random secret you generate | Authenticates inbound skill requests in `X-Kakao-Bot-Secret` | The Bot secret is required on deployed Make Agent Fast environments because Kakao skill calls do not provide a platform signature. Generate a unique high-entropy value and do not reuse the REST API key as the secret. ## Save the connector [#save-the-connector] Open the site's **Connectors** page, choose **KakaoTalk Channel**, enter all three values, and select **Connect**. Make Agent Fast validates the REST API key where Kakao permissions allow it and displays the connector's unique **Webhook URL** after save. Make Agent Fast cannot create the Open Builder skill or attach it to a block for you; the remaining work is in Kakao's console. ## Configure the Open Builder skill [#configure-the-open-builder-skill] 1. In Kakao i Open Builder, create a skill for the intended bot. 2. Set the skill URL to the connector's copied **Webhook URL**. 3. Add a custom request header named `X-Kakao-Bot-Secret` whose value exactly matches **Bot secret**. 4. Connect the skill to the fallback or scenario block that should invoke the agent. 5. Save and deploy/publish the bot configuration according to Kakao's workflow. The header is case-insensitive by HTTP rules, but the secret value is exact. Anyone who knows the webhook URL but not this secret should receive an unauthorized response. ## Test capabilities [#test-capabilities] Use the Open Builder test tool first, then the connected KakaoTalk Channel. Send a realistic Korean or supported-language text question and confirm the inline answer, **Last message**, and **Conversations** thread. The current skill contract is text-only. The agent reply is returned synchronously as Kakao `skillResponse` version `2.0` with one `simpleText` output, capped below Kakao's text limit. Voice, files, images, and separate outbound audio are not supported. ## Rotate or disconnect [#rotate-or-disconnect] To rotate the Bot secret, update the connector and the Open Builder custom header as one coordinated change, then test immediately. A mismatch stops every inbound request. Rotate the REST API key in Kakao Developers and Make Agent Fast before revoking the old key. Disabling or removing the connector does not delete the Open Builder skill or block. Remove or detach those provider-side objects when permanently decommissioning the integration. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ----------------------------------------------- | ------------------------------------------------------------------------------------- | | Connector requires a Bot secret | Generate one; deployed environments refuse an unauthenticated Kakao webhook | | REST API key rejected | Use the REST API key from the intended Kakao app and confirm its channel permissions | | Open Builder receives unauthorized | Add/update `X-Kakao-Bot-Secret` so it exactly matches the connector value | | Test tool shows a fallback instead of an answer | Confirm the block invokes the saved skill URL and inspect Kakao request/response logs | | Requests time out | Test a fast agent configuration; Kakao expects the skill response inline | | Voice or images are ignored | Expected; the current connector accepts text only | Do not expose the REST API key or Bot secret in client code or public documentation. --- # Messenger Source: /docs/connectors/messenger.md {/* docs-visuals */} ## Prepare the Meta app and Page [#prepare-the-meta-app-and-page] Follow Meta's official [Messenger Platform getting-started guide](https://developers.facebook.com/docs/messenger-platform/get-started). Add Messenger to a Meta app, connect the Facebook Page that should receive messages, and ensure your account can administer both assets. Collect values from the same app and Page: | Make Agent Fast field | Provider value | Purpose | | --------------------- | ---------------------------------------- | ---------------------------------------- | | **Page access token** | Token issued for the selected Page | Reads Page identity and sends replies | | **App secret** | 32-character hexadecimal Meta app secret | Verifies signed inbound webhook requests | Do not use a user access token or a token belonging to a different Page. Meta app development/review state controls which people can use the integration. ## Save and validate credentials [#save-and-validate-credentials] Open the site's **Connectors** page, select **Messenger**, enter the Page access token and app secret, and select **Connect**. Make Agent Fast calls the Graph API with `appsecret_proof`, resolves the Page ID/name, and binds inbound events to that exact Page. The connector card then displays its **Webhook URL** and **Verify token**. ## Configure the Page webhook [#configure-the-page-webhook] In the same Meta app: 1. Open the Messenger webhook settings. 2. Paste the connector **Webhook URL** and **Verify token**. 3. Complete the verification challenge. 4. Select the message event subscription required by the Messenger workflow. 5. Subscribe the intended Facebook Page to the app. 6. Confirm the Page subscription and app mode permit your test user. The verify token is used only for the initial challenge. Runtime events must carry a signature that matches the app secret. Make Agent Fast also rejects events addressed to a different Page even if they came from the same Meta app. ## Test capabilities [#test-capabilities] Send a normal message to the Page from a permitted, non-admin test account and confirm the response plus **Conversations** thread. Then test an audio attachment if inbound voice matters to your workflow. Text and supported inbound audio are processed. Replies are text-only in the current Messenger connector, even when the source message was voice. Delivery/read receipts, echoes of the Page's own messages, images, and other unsupported attachments are ignored. ## Production readiness [#production-readiness] Complete the permissions, app review, business verification, privacy URL, and Page-publication steps Meta requires for your audience. Development-mode success with an app-role account does not establish that the public can message the Page integration. Document the owning Meta app and Page ID for operators. A Page token can stop working when its owner, permissions, business association, or provider policy changes. ## Rotate or disconnect [#rotate-or-disconnect] Generate the replacement Page token, update the connector, test from a permitted user, then invalidate the old token. Update Make Agent Fast immediately after rotating the app secret. Disabling or removing the connector does not unsubscribe the Page in Meta. Remove the Page subscription and unused provider credential when permanently decommissioning the integration. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | --------------------------------------------- | -------------------------------------------------------------------------------------------- | | Token rejected | Confirm it is a Page token for the intended Page and the app secret belongs to the same app | | Webhook verification fails | Copy the current URL and verify token exactly; use public HTTPS | | Verification works but messages do not arrive | Subscribe the Page and message event, then check app mode, user role, and Meta delivery logs | | Requests are unauthorized | Correct the app secret; signed POST requests do not use the verify token | | Inbound message appears but no reply | Check Page token validity, messaging permissions, provider send error, and connector status | | Voice gets a text answer | Expected; outbound Messenger voice is not currently supported | Provider credentials belong only in the authenticated connector form or a trusted server-side API call. --- # Connector overview Source: /docs/connectors/overview.md {/* docs-visuals */} Connectors receive events from an external messaging platform, normalize text or supported audio, run the same site agent, and return a channel-appropriate response. Each external user gets channel-scoped conversation history visible in **Conversations**. ## Capability matrix [#capability-matrix] | Channel | Inbound text | Inbound voice | Outbound text | Outbound voice | Provider setup after save | | --------- | --------------- | ------------- | --------------------- | -------------- | ------------------------------------------------------------------ | | Telegram | Yes | Yes | Yes | Optional | Webhook registered automatically | | WhatsApp | Yes | Yes | Yes | Optional | Add Meta webhook and `messages` subscription | | Messenger | Yes | Yes | Yes | No | Add Meta webhook and subscribe the Page | | Discord | `/ask` question | No | `/ask` response | No | Paste the interactions endpoint; command registration is automatic | | KakaoTalk | Yes | No | Inline skill response | No | Add the skill URL, header, and block in Open Builder | “Voice in” means a supported voice/audio message is downloaded and transcribed. “Voice out” requires a working speech provider and the connector's **Reply with voice** option. Unsupported attachments such as images and locations are ignored rather than interpreted as agent instructions. ## Before connecting [#before-connecting] 1. Test the intended site agent in the dashboard. Publishing is recommended so its public links and pages work; a draft connector can still answer. 2. Configure a working model key under **Settings → AI provider keys**. Configure speech keys before testing voice. See [Provider keys](/docs/account/provider-keys). 3. Create a dedicated app, bot, number, or channel in the external provider. Do not reuse a credential owned by an unrelated production integration. 4. Confirm your deployment has a public HTTPS connector base URL. Provider webhooks cannot deliver to `localhost` without a secure development tunnel. 5. Open the site's **Connectors** page and choose the channel. On save, Make Agent Fast calls the provider to validate the submitted credential. A connected label means the credential validation and any automatic provisioning completed; it does not prove that a manual Meta, Discord, or Kakao console step was completed. ## Credential handling [#credential-handling] Connector forms accept provider credentials only from the authenticated dashboard or the server-side Public API. Secrets are encrypted at rest and never returned in connector list responses. Treat the following values as passwords: * Telegram bot token * Meta access tokens and app secret * Discord bot token * Kakao REST API key and bot secret Do not place them in browser JavaScript, screenshots, support tickets, or source control. The webhook URL and Meta verify token are configuration values intended to be copied to the matching provider console; they do not replace provider credentials. ## Connect through the API [#connect-through-the-api] Use a server-held MAF API key with `connectors:write`. The channel-specific `credentials` object is write-only. The response omits plaintext secrets and includes the webhook URL needed for manual provider setup. ```bash curl -X POST "https://makeagent.fast/api/v1/sites/$SITE_ID/connectors" \ -H "Authorization: Bearer $MAF_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: connector-telegram-v1" \ -d '{ "channel": "telegram", "credentials": { "bot_token": "'$TELEGRAM_BOT_TOKEN'" }, "config": { "reply_with_voice": false } }' ``` Saving the same channel replaces its credential/configuration while preserving its routing token. Use [API authentication](/docs/api/authentication), [Scopes and errors](/docs/api/scopes-errors), and the [API reference](/docs/api/reference) for production integration rules. ## Test and observe [#test-and-observe] Use a provider test user and a realistic question that depends on the agent's knowledge. Confirm all of the following: * The provider accepts or verifies the webhook endpoint. * A message reaches the agent and receives the expected response. * **Last message** changes on the connector card. * The thread appears in **Conversations** with the correct channel. * Any provider review/development-mode restriction is understood before public launch. * Voice is tested separately from text when the channel supports it. The connector card surfaces the last provider or delivery error. Provider dashboards also retain delivery logs; compare timestamps and event identifiers when only one side reports the request. ## Disable, rotate, or remove [#disable-rotate-or-remove] **Disable** stops processing while retaining the connector record. Telegram's webhook is removed on disable and registered again on enable; console-configured provider subscriptions remain and should also be paused provider-side if necessary. **Remove** deletes the Make Agent Fast connector and attempts best-effort provider teardown, but it does not delete the external app, revoke its credential, or erase existing conversation history. For a rotation, create the new provider token, update the connector, test it, then revoke the old token. If a credential was exposed, revoke it at the provider first and treat the interval as a security incident. ## Common failures [#common-failures] | Symptom | Check | | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | Credentials do not validate | Correct app/bot, token type, expiry, required provider permission, and copied characters | | Webhook verification fails | Public HTTPS URL, exact verify token, correct provider product, and request-signing secret | | Verification succeeds but no messages arrive | Event subscription, Page/number/channel association, provider app mode, and connector enabled state | | Text works but voice fails | Channel capability, speech/transcription key, supported audio, media permission, and provider download expiry | | Replies stop after rotation | New credential saved, old credential revoked only after testing, and manual provider subscription still points to this webhook | Continue with the channel guide for the exact provider fields and console sequence. --- # Telegram Source: /docs/connectors/telegram.md {/* docs-visuals */} ## Create and prepare the bot [#create-and-prepare-the-bot] Follow Telegram's official [Introduction to Bots](https://core.telegram.org/bots) and [BotFather guide](https://core.telegram.org/bots/features#botfather): 1. Open the verified **@BotFather** account in Telegram. 2. Run `/newbot`, choose a display name, and choose a unique username ending in `bot`. 3. Copy the bot token. Anyone with this token controls the bot. 4. Optionally configure the photo, description, about text, and localized command help in BotFather before launch. The token is for the Telegram Bot API; it is not a Telegram login code, API ID, or API hash. ## Connect in Make Agent Fast [#connect-in-make-agent-fast] Open the site's **Connectors** page, choose **Telegram**, paste **Bot token**, choose whether supported replies should also be spoken, and select **Connect**. Make Agent Fast calls Telegram `getMe` to validate the token and display the bot username. It then calls `setWebhook` with the connector's public HTTPS URL, a secret verification token, and `message` as the accepted update type. Existing pending updates are not dropped. No manual webhook step is normally required. Saving or re-enabling the connector safely registers the same webhook again. ## Test the conversation [#test-the-conversation] 1. Open the bot's `t.me` link from a non-owner Telegram account. 2. Select **Start**; bots cannot initiate a conversation with a user. 3. Send a knowledge-based text question. 4. Send a voice note if voice is enabled. 5. Confirm replies arrive, **Last message** changes, and the thread appears in **Conversations**. Group behavior depends on Telegram Privacy Mode and whether the bot is added to that group. Test direct messages first so group visibility rules do not hide the update. ## Voice behavior [#voice-behavior] Telegram supports inbound voice and outbound voice in this connector. Inbound voice is downloaded and transcribed before the agent turn. With **Reply with voice** enabled and a speech provider available, Make Agent Fast sends a voice/audio response in addition to text. If transcription or speech fails, ask the user to retry as text and inspect the connector error. ## Rotate, disable, or remove [#rotate-disable-or-remove] If the token is exposed, generate/revoke it in BotFather immediately, update the connector with the replacement, and test again. Disabling or removing the connector calls Telegram `deleteWebhook` without discarding pending updates. Deleting the Make Agent Fast connector does not delete the Telegram bot itself. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ---------------------------------------------- | ------------------------------------------------------------------------------------- | | Token rejected on save | Copy the current BotFather token; remove whitespace and ensure it belongs to this bot | | Messages reach an older service | Re-save the connector so Make Agent Fast replaces the Telegram webhook | | Direct messages work but group messages do not | Review BotFather Privacy Mode and group permissions | | Bot does not message a new user first | Expected Telegram behavior; ask the user to open the bot and select **Start** | | Text works but voice fails | Check model/transcription/speech keys and retry with a supported Telegram voice note | Never paste a live token into documentation, source code, analytics, or a support screenshot. --- # WhatsApp Source: /docs/connectors/whatsapp.md {/* docs-visuals */} ## Prepare the Meta app and number [#prepare-the-meta-app-and-number] Use Meta's official [WhatsApp Cloud API getting-started guide](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) to create or select a Meta app, add the WhatsApp product, and associate a WhatsApp Business Account and phone number. Collect these three values from the same app and number: | Make Agent Fast field | Provider value | Notes | | --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------- | | **Phone number ID** | Numeric Phone Number ID | This is not the visible `+` phone number or WABA ID | | **Access token** | Cloud API access token | Temporary dashboard tokens expire; use an appropriate long-lived system-user token for production | | **App secret** | Meta app secret | A 32-character hexadecimal secret used for webhook signature verification | Grant only the permissions needed for the number and messaging workflow. Keep development/test assets separate from the production number. ## Save and validate credentials [#save-and-validate-credentials] Open the site's **Connectors** page, select **WhatsApp**, enter all three values, choose **Reply with voice** if required, and select **Connect**. Make Agent Fast validates the number through the Graph API using the access token plus `appsecret_proof`; this catches a wrong number, token, or app secret before webhook traffic starts. The connector card then reveals a unique **Webhook URL** and **Verify token**. ## Configure the Meta webhook [#configure-the-meta-webhook] In the same Meta app: 1. Open the WhatsApp webhook configuration. 2. Paste the connector **Webhook URL** as the callback URL. 3. Paste the connector **Verify token** exactly; it is case-sensitive. 4. Complete verification. 5. Subscribe the WhatsApp Business Account/number to the `messages` webhook field. 6. Confirm the app and business assets are available to the intended test or production users. The verify token proves ownership during the GET verification challenge. Runtime POST requests are separately authenticated with the Meta app-secret signature; do not substitute one for the other. ## Test text and voice [#test-text-and-voice] Send a message from an allowed test number first. During Meta development mode, unregistered users may not be able to reach the app. Confirm the thread and channel in **Conversations** and watch **Last message** on the connector card. Inbound text and voice are supported. With **Reply with voice** enabled and speech configured, Make Agent Fast uploads generated audio through the Cloud API and sends it alongside the text response. Images, locations, contacts, and other unsupported message types are ignored. ## Production readiness [#production-readiness] Before advertising the number, complete the Meta business verification, app review, display-name, template, and messaging-policy steps required for your use case. Those requirements are controlled by Meta and can change independently of Make Agent Fast. Use a production credential with an intentional owner and expiry/rotation procedure. Record which Meta app, system user, WABA, and Phone Number ID belong to this connector so an operator can rotate it without guessing. ## Rotate or disconnect [#rotate-or-disconnect] Create a replacement token with the same asset access, update the connector, send a test message, and only then revoke the old token. If the app secret changes, update it in Make Agent Fast immediately or every signed inbound webhook will fail. Disabling or removing the connector does not unsubscribe or delete the Meta webhook configuration. Remove the provider subscription too when permanently decommissioning the number. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | Credentials rejected | Confirm the token can read the exact Phone Number ID and the app secret is from the same app | | Meta cannot verify the callback | Use the copied HTTPS URL and exact verify token; ensure the connector is still present | | Verification succeeds but no messages arrive | Subscribe `messages`, check WABA/number association, app mode, test-user access, and provider delivery logs | | Webhooks return unauthorized | Re-enter the correct app secret; Meta POST signatures do not use the verify token | | Outbound replies fail | Check token expiry, number permissions, recipient policy/window, and Graph API errors | | Text works but audio fails | Check transcription/speech provider keys and the token's media upload/download access | Never expose the access token or app secret in browser code. Use the Public API only from a trusted server when automating connector creation. --- # Astro Source: /docs/embed/astro.md {/* docs-visuals */} ## Prepare the deployment origin [#prepare-the-deployment-origin] Publish the Make Agent Fast site, enable embedding, and allowlist the exact Astro development, preview, and production origins. The final scheme/hostname/port in the browser matters, not the build host or repository URL. ## Add to a shared layout [#add-to-a-shared-layout] Place the standard external loader immediately before `` in the Astro layout that should own the launcher. ```astro --- // src/layouts/BaseLayout.astro --- ``` `is:inline` tells Astro to preserve the external script tag and its `data-*` attributes instead of bundling/transforming it. The loader remains public and framework-independent; do not import it into a server module. ## Limit to selected pages [#limit-to-selected-pages] Use a nested layout or include the tag only on selected `.astro` pages. Avoid placing the same tag in both a base layout and page. The loader ignores a duplicate same-slug root, but the duplicate HTML makes navigation and teardown difficult to reason about. If Astro client-side view transitions are enabled, test direct page loads plus in-app navigation. A persistent shared layout is preferred for a site-wide agent. For route-specific behavior, remove both the script and `maf-embed-root-YOUR_SITE_SLUG` when the route is left before inserting a fresh script later. ## Configure CSP and consent [#configure-csp-and-consent] Static hosting headers still control the browser. Allow `https://makeagent.fast` in `script-src`, `frame-src`, and the relevant connection policy. If a consent manager blocks the script, load the tag only after required consent rather than weakening CSP or bypassing consent. ## Verify the built output [#verify-the-built-output] 1. Run the Astro production build and inspect the emitted HTML for the preserved `data-agent` tag. 2. Open the deployed page in a private window. 3. Confirm one embed root and successful loader/iframe requests. 4. Navigate across Astro routes and confirm the intended persistence. 5. Send a real message and test mobile/microphone behavior. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ------------------------------------------ | ----------------------------------------------------------- | | Built HTML omits/changes attributes | Add `is:inline` to the external script | | Two script tags are emitted | Keep installation in one owning layout/page | | Client navigation differs from direct load | Test view transitions and choose a persistent shared layout | | Works in dev but not static host | Add the exact production origin and hosting CSP headers | | Voice fails | Configure HTTPS, browser permission, and Permissions Policy | No npm Astro adapter is currently published; use the hosted loader shown here. --- # Framer Source: /docs/embed/framer.md {/* docs-visuals */} ## Code component availability [#code-component-availability] Copy `integrations/framer/MakeAgentFast.tsx` into a Framer code component and choose the published agent. The source exposes platform URL, position, and label controls. This is manually copied source, not a published Framer Marketplace component. A listing URL will appear here only after an authorized owner publishes and verifies it. ## Custom code [#custom-code] Add the standard loader in **Site settings → Custom code** at the end of the body. ```html ``` Publish before testing. Framer's canvas and preview host can use different origins from the final custom domain. --- # Ghost Source: /docs/embed/ghost.md {/* docs-visuals */} Open **Settings → Code injection** and add the script to the site footer. ```html ``` Use post-level code injection only when the agent should appear on selected posts. If you modify a theme instead, place the loader in `default.hbs` before `` and re-check it after theme updates. Allowlist the public publication origin and clear any edge cache before testing a changed slug. --- # HTML and JavaScript Source: /docs/embed/html.md {/* docs-visuals */} ## Prepare the site [#prepare-the-site] Publish the agent's site, open **Embed**, enable embedding, and add the final host-page origin. Add development and production separately; `http://localhost:3000`, `https://example.com`, and `https://www.example.com` do not match one another. ## Install the script [#install-the-script] Paste the loader immediately before the closing `` tag on every page that should show the launcher. Replace only the public site slug and optional presentation values. ```html ``` `data-agent` is required. The slug is public; no API key belongs in this tag. Omit optional attributes to inherit the site's appearance and automatic theme behavior. See [Embed overview](/docs/embed/overview) for every accepted value. ## Choose site-wide or page-specific placement [#choose-site-wide-or-page-specific-placement] For a shared server-side template, place the script in the global layout/footer. For selected pages, include it only in those templates. Load the same slug once per document; duplicate same-slug tags are ignored, but removing duplicates keeps ownership and teardown predictable. Do not paste the snippet into an HTML sanitizer or rich-text field that strips ` ``` Only `data-agent` is required. Do not put a MAF API key, provider key, connector token, visitor identity, or private customer data in any `data-*` attribute. ## Configure attributes [#configure-attributes] | Attribute | Accepted value | Default | Effect | | --------------- | ------------------------------------- | --------------------- | ------------------------------------------------- | | `data-agent` | Published site slug | Required | Selects the site and agent to load | | `data-position` | `left` or `right` | `right` | Anchors the launcher and panel | | `data-label` | Plain text | Icon only | Adds visible text and supplies the launcher label | | `data-accent` | CSS color such as `#6d5ce7` | Site appearance | Overrides the launcher background | | `data-locale` | Supported locale such as `en` or `ko` | Site/browser behavior | Requests the widget UI language | | `data-theme` | `light` or `dark` | Host/OS detection | Pins the initial widget theme | Without a theme override, the loader samples the host background and then falls back to the visitor's OS preference. It also passes the host font family to the frame and fetches the latest launcher appearance. A failed appearance request does not block the launcher; it keeps the safe default style. ## Runtime behavior [#runtime-behavior] * Load the script once per document. Repeating the same slug is idempotent and still produces one `maf-embed-root-YOUR_SITE_SLUG` root. * Different agents may coexist on one document. The loader marks each processed script with `data-maf-bootstrapped` so an async fallback initializes every distinct `data-agent` tag exactly once. * The iframe is lazy and hidden until the visitor opens it. It requests microphone, autoplay, and clipboard-write permission only through the browser's iframe permission model. * In a client-side routed app, mount the loader in the highest layout that should own it so route changes do not reload the conversation. * Removing only the script element does not remove an already-created launcher. For route-specific teardown, also remove the matching `maf-embed-root-...` element. Reload the script after teardown to mount it again. * The frame and loader exchange only namespaced lifecycle messages. The loader rejects messages that do not come from the Make Agent Fast origin and current site slug. ## Verify the installation [#verify-the-installation] Test the public page, not only a website-builder editor preview: 1. Open the deployed URL in a private window and confirm its exact origin is allowlisted. 2. Confirm `https://makeagent.fast/embed.js` returns `200` in the Network panel. 3. Confirm a single element with `data-maf-embed="YOUR_SITE_SLUG"` exists. 4. Open the launcher and confirm the iframe request to `/embed/YOUR_SITE_SLUG` succeeds. 5. Send a realistic question and confirm it appears in **Conversations**. 6. Test a narrow mobile viewport and, if enabled, microphone permission. ## Diagnose a missing launcher [#diagnose-a-missing-launcher] | Symptom | Likely cause | Fix | | ---------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------- | | Console warns `missing data-agent` | The slug attribute is absent or on a different script | Put `data-agent` on the `embed.js` tag | | `embed.js` is blocked | CSP or a consent manager rejected the script | Allow the script origin or load it after the required consent | | Launcher appears but the frame refuses to load | Site is unpublished, embedding is off, or parent origin differs | Recheck all three values in the dashboard | | Works on localhost but not production | Only the development origin is allowlisted | Add the exact deployed HTTPS origin | | Two different agents appear | The document loads two different slugs | Keep only the intended site slug | | Chat opens but voice does not | Browser permission or Permissions Policy blocks microphone | Follow [Security and CSP](/docs/embed/security) | ## Choose an installation guide [#choose-an-installation-guide] Use [HTML and JavaScript](/docs/embed/html) for a normal script tag. For React, Vue, SvelteKit, Astro, or a website builder, choose the matching guide in the sidebar. Platforms that do not allow arbitrary JavaScript, such as many Notion and Linktree surfaces, should link to the published hosted agent instead. --- # React and Next.js Source: /docs/embed/react-next.md {/* docs-visuals */} ## Prepare origins and scope [#prepare-origins-and-scope] Publish the site, enable embedding, and allowlist the exact local/preview/production origins. Decide whether the launcher should survive every client navigation (root layout) or exist only under selected routes (nested layout/component). ## Next.js App Router [#nextjs-app-router] Use `next/script` in a component rendered by the layout that owns the launcher. The slug is safe to expose; do not use an API key or secret environment variable. ```tsx import Script from "next/script"; export function AgentEmbed() { return ( ``` Allowlist the canonical storefront origin. Preview domains may differ from the live `myshopify.com` or custom domain and must be added separately for testing. ## Theme changes [#theme-changes] Re-check the app embed after publishing a new theme. Test the launcher around cart drawers, consent banners, and mobile navigation so fixed elements do not overlap. --- # Squarespace Source: /docs/embed/squarespace.md {/* docs-visuals */} For a site-wide launcher, paste the snippet in **Settings → Advanced → Code Injection → Footer** where your Squarespace plan supports it. ```html ``` For selected pages, use a supported code block or page-specific injection. Test after saving and on the public domain; editor mode can suppress custom scripts. If a cookie-consent configuration blocks third-party scripts, categorize and load the embed according to your consent policy. --- # SvelteKit Source: /docs/embed/sveltekit.md {/* docs-visuals */} ## Prepare the origin [#prepare-the-origin] Publish the site, enable embedding, and allowlist each exact SvelteKit development, preview, and production origin. Use a root layout for a site-wide launcher or a nested layout/page component for selected routes. ## Create the component [#create-the-component] `onMount` runs only in the browser. Return a cleanup function when the owning layout/component can unmount. ```svelte ``` Mount the component once from `src/routes/+layout.svelte` for the entire application. For a route group, place it in that group's `+layout.svelte`; cleanup removes the launcher when the visitor leaves the group. ## Use configuration safely [#use-configuration-safely] The public site slug can be a SvelteKit public environment value because it is not a credential. Do not use `$env/static/public` or browser code for MAF API keys, AI-provider keys, or connector secrets. Keep those in server-only modules/routes. ## Preserve or reset conversation state [#preserve-or-reset-conversation-state] Keeping the component in the root layout preserves the same iframe across client navigation. Route-scoped cleanup deliberately discards that iframe and its current in-page state. Choose one behavior rather than remounting on every page. ## Verify the production build [#verify-the-production-build] 1. Run the SvelteKit production adapter/build. 2. Direct-load the public route and confirm one launcher/root. 3. Navigate into and out of the owning layout. 4. Inspect Network/CSP and the exact parent origin. 5. Test a real message, narrow viewport, and microphone when enabled. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ------------------------------------------- | -------------------------------------------------------------------- | | `document` error during SSR | Keep all loader code inside `onMount` | | Launcher persists after layout exit | Return cleanup and remove the generated root | | Conversation resets on every page | Mount in a persistent parent `+layout.svelte` | | Duplicate launcher guard prevents new mount | Ensure prior cleanup removed the old root before adding a new script | | Production only fails | Allowlist the final HTTPS origin and inspect adapter CSP headers | No npm SvelteKit adapter is currently published; the hosted script is the supported integration surface. --- # Vue and Nuxt Source: /docs/embed/vue-nuxt.md {/* docs-visuals */} ## Prepare the embed [#prepare-the-embed] Publish the site, enable embedding, and add the exact origin for local, preview, and production deployments. Decide whether the agent belongs to the whole application or only selected route components. ## Nuxt site-wide plugin [#nuxt-site-wide-plugin] Create a `.client.ts` plugin so no DOM code runs during server rendering. Nuxt loads it once in the browser, which keeps the launcher and conversation stable across client navigation. ```ts // plugins/make-agent-fast.client.ts const slug = "YOUR_SITE_SLUG"; export default defineNuxtPlugin(() => { if (document.getElementById(`maf-embed-root-${slug}`)) return; const script = document.createElement("script"); script.id = "maf-agent"; script.src = "https://makeagent.fast/embed.js"; script.dataset.agent = slug; script.dataset.position = "right"; script.dataset.label = "Ask us"; script.async = true; document.body.appendChild(script); }); ``` Do not put a secret in `runtimeConfig.public`; only the public site slug and visual settings belong in the browser. Real MAF API/provider keys stay in private server runtime configuration. The site-wide plugin intentionally has no unmount cleanup: it belongs to the Nuxt application lifetime. During hot-module replacement in development, a prior root can remain until a full reload; the root guard prevents a second launcher. Verify final behavior with a clean production build rather than repeatedly editing the plugin in one long-lived tab. ## Route-scoped Vue component [#route-scoped-vue-component] For selected routes, mount in `onMounted` and remove both the script and generated root in `onBeforeUnmount`. ```vue ``` Use one component owner. Multiple components with different slugs intentionally create multiple agents, which is rarely desirable. If a Vue application switches authenticated tenants without a full page load, treat that as an explicit teardown/remount. Remove the old slug's root, validate the new tenant's public slug on trusted application state, then append a new script. Changing `script.dataset.agent` after the loader ran does not retarget the existing iframe. Host security headers still apply to Nuxt/Vue output. Add the loader/frame origin to CSP and permit microphone only when voice is enabled; a client-only plugin cannot override a response-level browser policy. ## Verify SSR and routing [#verify-ssr-and-routing] 1. Run the production Nuxt/Vue build and confirm no `document is not defined` error. 2. Direct-load a route with the embed and confirm one root. 3. Navigate client-side and confirm site-wide versus route-scoped behavior. 4. Inspect `embed.js`, iframe, CSP, and exact origin. 5. Send a message and test mobile/microphone behavior. ## Troubleshooting [#troubleshooting] | Symptom | Fix | | ------------------------------------ | ---------------------------------------------------------------------- | | SSR crashes | Rename the Nuxt plugin to `.client.ts` or keep DOM work in `onMounted` | | Launcher remains after leaving route | Remove the slug-specific root in `onBeforeUnmount` | | Launcher resets on every route | Use the site-wide client plugin/layout instead of a page component | | Preview domain fails | Add that exact HTTPS origin separately | | Script is inserted twice | Use one stable owner and root/ID guard | No npm Vue/Nuxt adapter is currently published; this guide uses the supported hosted loader directly. --- # Webflow Source: /docs/embed/webflow.md {/* docs-visuals */} ## Availability [#availability] The supported integration is custom code. There is no public Make Agent Fast Webflow Marketplace app today. The repository's `integrations/webflow` folder contains the same footer snippet shown below, not a native Webflow application. ## Custom code [#custom-code] Add the generated script to **Site settings → Custom code → Footer code**, then publish. ```html ``` Custom code may not execute inside the Webflow Designer preview. Test the published staging and production domains and allowlist each origin you use. --- # Wix Source: /docs/embed/wix.md {/* docs-visuals */} ## Custom-element availability [#custom-element-availability] Use the custom element in `integrations/wix/make-agent-fast.js` or Wix's approved body-end custom-code surface. This source is not a public Wix App Market application. A listing URL will appear here only after external review approves and publishes one. ## Custom code [#custom-code] In site settings, add the generated loader as body-end custom code and apply it to the intended pages. ```html ``` Wix may serve preview and live sites on different hosts. Add only the hosts you need to the Embed allowlist and re-publish after changes. --- # WordPress Source: /docs/embed/wordpress.md {/* docs-visuals */} ## Manual plugin source [#manual-plugin-source] The manually installable plugin source is in `integrations/wordpress/make-agent-fast`. It is not listed in the WordPress.org directory. Zip that folder, upload it in **Plugins → Add New → Upload Plugin**, then configure the published slug under **Settings → Make Agent Fast**. A directory link will appear here only after external review approves it. ## Custom HTML [#custom-html] If you install manually, add the standard script using a child theme, a trusted header/footer script tool, or a Custom HTML block that your WordPress plan permits. ```html ``` Add both the production origin and any separate staging origin to the dashboard allowlist. Clear page and CDN caches after changing the snippet. A parent-theme update can overwrite direct edits. Use the manual plugin or a child theme for durable installation. --- # Core concepts Source: /docs/getting-started/concepts.md {/* docs-visuals */} ## Workspace [#workspace] Your account is the workspace boundary for sites, billing, API credentials, and team operations. Access to one workspace never grants access to another. ## Site [#site] A site is the publishable unit. It owns its page content, visual style, domain, agent, knowledge, connectors, analytics, and conversations. A plan can limit how many sites you may keep active. ## Agent [#agent] The agent is the conversational behavior attached to a site. Its persona and instructions determine how it speaks; its knowledge determines which facts it can ground answers in. ## Knowledge [#knowledge] Knowledge sources are indexed material such as notes, URLs, files, FAQs, and products. They support answers but do not replace behavioral instructions. Update the source when a fact changes. ## Channel [#channel] A channel is where a visitor talks to the agent: the hosted site, an embedded widget, voice, Telegram, WhatsApp, Messenger, Discord, or KakaoTalk. Capabilities vary by channel. ## Credits and entitlements [#credits-and-entitlements] AI operations consume credits. Live voice minutes, voice cloning, site counts, and branding controls have separate plan entitlements. API requests that trigger AI work use the same underlying account limits. ## Publish state [#publish-state] Draft changes are private until published. Publishing makes the current site available at its public URL. Connector and embed behavior uses the current published or configured agent state as described in each feature guide. --- # Dashboard tour Source: /docs/getting-started/dashboard.md {/* docs-visuals */} The dashboard is organized around sites. Use the site switcher before changing content, credentials, or operational settings. ## Build [#build] **Build** edits the hosted page. **Agent** controls persona, knowledge, FAQs, products, language, lead behavior, and voice. **Appearance** controls the customer-facing presentation. ## Reach [#reach] **Connectors** links messaging channels. **Domain** manages the hosted address and custom domains. **Embed** provides installation code and origin controls for an existing website. ## Operate [#operate] **Conversations** collects web and connector threads. **Analytics** shows traffic and engagement. **Broadcasts** sends approved audience messages, while **Notifications** is the in-dashboard feed for lead, handoff, and booking events; it does not configure external alerts. ## Account controls [#account-controls] **Usage** shows credits and plan limits. API keys are account-level credentials and should be created only for applications you trust. The active site name appears in the site switcher. Confirm it before deleting knowledge, rotating connector secrets, publishing, or changing a domain. --- # Quickstart Source: /docs/getting-started/quickstart.md {/* docs-visuals */} This guide takes you from a new account to a working, shareable agent. You do not need to write code. ## Before you begin [#before-you-begin] You need an account, a clear description of what the agent should do, and at least one useful source such as a webpage, document, FAQ, or set of notes. ## 1. Create a site and agent [#1-create-a-site-and-agent] 1. Open the dashboard and choose **New site**. 2. Describe the person, business, product, or service the agent represents. 3. Choose a starting template. 4. Review the generated headline, sections, links, and contact details. Keep the first version narrow. A support agent for one product is easier to evaluate than an agent expected to answer every question about a company. ## 2. Define the agent [#2-define-the-agent] Open **Agent** and set: * Identity: who the agent represents * Goal: the outcome it should help visitors reach * Tone: direct, friendly, technical, concise, or another appropriate style * Boundaries: topics it must refuse, escalate, or answer carefully * Contact behavior: when to collect a lead or suggest human follow-up Use direct statements such as “Never invent shipping dates” and “Ask for an email only after the visitor requests a quote.” Avoid vague instructions like “be good.” ## 3. Add knowledge [#3-add-knowledge] Add one or more knowledge sources, then wait for processing to finish. Prefer current, authoritative material. Remove duplicate or outdated sources so the agent does not have to choose between conflicting facts. ## 4. Test realistic questions [#4-test-realistic-questions] Use the tester to ask at least five questions: * A common customer question * A question whose answer appears deep in a source * A question the agent should not answer * A request that should capture a lead * A question in every language you plan to support Correct the source or instruction that caused a weak response, then test again. ## 5. Publish [#5-publish] Choose **Publish**. Your site becomes available on its Make Agent Fast subdomain. You can then connect a custom domain, add the agent to an existing website, or connect a messaging channel. ## Next steps [#next-steps] * [Customize the appearance](/docs/build/appearance) * [Embed the agent](/docs/embed/overview) * [Connect a custom domain](/docs/publish/custom-domains) * [Review conversations and leads](/docs/operate/conversations) --- # Analytics Source: /docs/operate/analytics.md {/* docs-visuals */} ## Define the measurement window [#define-the-measurement-window] Open a site's **Analytics** page and choose 7, 30, 90 days, or a custom start/end date. Select the primary metric, optional channel/source filter, and **Compare with previous period** when you need a like-for-like baseline. Filters apply to the report and URL so a view can be reproduced. Use equivalent campaign length, weekdays, seasonality, and publication state when interpreting a comparison. A large percentage from one or two events is not reliable evidence. ## Understand the main metrics [#understand-the-main-metrics] | Metric | Meaning | | ----------------- | ---------------------------------------------------------------------------------------------------- | | **Visits** | Recorded public page views, deduplicated where the analytics model can identify the same visitor/day | | **Conversations** | A conversation start rather than every individual message turn | | **Leads** | Captured lead events/records in the selected context | | **Conversion** | Visit-to-lead ratio; also inspect visit-to-conversation and conversation-to-lead | Message volume and average messages per conversation are engagement measures, not additional conversation starts. Returning-visitor, unanswered, handoff, booking, and qualified-lead measures describe different parts of the journey. ## Read trends, funnel, and time [#read-trends-funnel-and-time] Use the daily trend to find a change point, then compare it with releases, campaigns, source updates, provider incidents, and publishing changes. The funnel separates three questions: did visitors start a conversation, did conversations create leads, and did visits create leads overall? Hourly and weekday views show when recorded activity occurred. They are useful for staffing and campaign timing, but timestamps, audience geography, and small samples can distort a simple “best time” conclusion. ## Inspect acquisition and channels [#inspect-acquisition-and-channels] Top sources use referrer/UTM information when available. Direct/none includes traffic whose source is absent or hidden. Browser privacy, link apps, redirects, consent controls, and cross-device journeys make attribution incomplete; do not force unattributed visits into a campaign. Channel performance compares recorded text, voice, and real-time activity. Messaging connector conversations contribute to the broader operating picture, but not every external platform exposes the same page-view or attribution baseline, so cross-channel conversion rates are not perfectly equivalent. ## Find knowledge and conversation gaps [#find-knowledge-and-conversation-gaps] The questions table shows frequently asked topics and unresolved/gap counts. **What your agent grounded on** shows recent answers plus recorded knowledge sources. Use both with the actual **Conversations** inbox: 1. Find a high-volume or high-gap question. 2. Read several complete threads. 3. Decide whether the problem is missing knowledge, conflict, persona, language, UI, or visitor intent. 4. Make one controlled change. 5. Rerun acceptance tests and compare a new period. A metric indicates where to investigate; it does not explain the cause by itself. ## Manage leads and follow-up [#manage-leads-and-follow-up] The lead area shows captured leads, supports safe export/delete actions, and can create reviewed follow-up. Filtered lead attribution can use newer lead events while the lead inbox still shows all captured lead records; read the note shown by the dashboard before assuming counts should match exactly. ## Export the report [#export-the-report] Use **Export** to create Markdown (`.md`), Excel (`.xlsx`), or a print/PDF view for the current filters. The PDF option opens a print-optimized page; choose **Save as PDF** in the browser. Record the filter/date context with the exported file and protect any visitor/lead data it contains. Exports are snapshots. They do not continue updating and should not be treated as a separate source of truth after dashboard filters or underlying events change. ## Privacy and data quality [#privacy-and-data-quality] Collect only the analytics needed for a defined purpose and reflect relevant tracking in visitor disclosures. Consent blocking, privacy controls, offline delivery, duplicate suppression, provider gaps, and older events can all produce incomplete data. Use the dashboard's last-event timestamp to distinguish “zero” from “not recently receiving events.” ## Troubleshooting [#troubleshooting] | Symptom | Check | | ---------------------------------------------- | ------------------------------------------------------------------------------------------ | | Report is empty | Correct site/date/filter, published traffic, last event, consent blocking, and public test | | Visit/conversation counts differ from messages | Expected; starts/views are deduplicated differently from message turns | | Filtered leads differ from inbox total | Filtered event attribution versus all stored lead records | | Source is direct/none | Missing/hidden referrer or UTM; reproduce with a tagged public link | | Export differs from screen | Same filters/date at export time and whether live data changed afterward | --- # Broadcasts Source: /docs/operate/broadcasts.md {/* docs-visuals */} ## Know the current scope [#know-the-current-scope] The dashboard broadcast feature currently targets **Kakao AlimTalk**. It is not a general Telegram/WhatsApp/Messenger/Discord campaign composer. A live AlimTalk gateway requires a registered sender profile, approved template, configured platform provider, and a reachable Korean phone number for each recipient. The recipient count is resolved from non-erased leads on the current site with a reachable contact value, deduplicated by contact. Development/mock mode may accept an email fallback, but a real AlimTalk gateway needs an appropriate phone number. Confirm the displayed mode is live before treating a queued message as real delivery. ## Understand the send boundary [#understand-the-send-boundary] Selecting **Send** queues one durable job for every currently eligible recipient. The dashboard does not currently provide per-send segmentation, recipient editing, scheduled time, preview audience, pause, or recall. Do not use the button until the entire resolved audience is intended and authorized to receive the message. The form accepts an approved template code and a body up to 2,000 characters. A provider can enforce a shorter template, exact approved wording/variables, timing, sender-profile, and policy limits. Make Agent Fast queuing does not override those rules. ## Prepare a compliant broadcast [#prepare-a-compliant-broadcast] 1. Confirm the site lead-capture flow requested the relevant contact and consent/lawful basis. 2. Remove or erase people who should not be contacted before resolving the audience. 3. Confirm a live AlimTalk provider and registered sender key are configured. 4. Obtain the provider-approved template code and match the body to that template. 5. Verify sender identity, purpose, links, required disclosures, and opt-out path. 6. Use a separate test site/audience if you need a small production test; the current composer targets all eligible leads on its site. Do not send credentials, sensitive account details, protected records, invented personalization, or an offer the agent/site has not approved. ## Queue and observe [#queue-and-observe] After sending, history shows the body, status, intended recipient count, and successful send count. Jobs are idempotent per broadcast and lead so a queue retry should not intentionally create a second copy of the same broadcast/recipient pair. “Sent” means the configured gateway accepted all tracked sends. It does not prove phone delivery, reading, conversion, or valid marketing consent. Use gateway delivery receipts and business reporting where available. If some jobs fail, the broadcast retains the latest provider error while successful sends increment independently. Do not create a new identical broadcast until you understand whether the provider accepted any earlier attempts. ## API automation [#api-automation] Use `broadcasts:write` to create a draft and queue it through the Public API from a trusted server. Preserve idempotency and review recipient resolution/provider readiness. The API cannot make an unapproved AlimTalk template compliant. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Send button is disabled | No eligible site leads with a reachable contact | | Mode shows local/dev | Live AlimTalk provider/credentials are not configured; no real customer delivery | | Gateway rejects template | Approved template code, body/variables, sender profile, and recipient format | | Recipient count includes unusable contact | Real delivery needs phone; clean lead capture/data before sending | | History remains sending | Queue/provider error and `sent count` versus recipient count | | Need to contact only a segment | Not currently supported in this composer; create an approved separate audience workflow rather than sending all | Stop sending when complaints, unexpected recipients, template mismatch, or provider errors appear. --- # Conversations and leads Source: /docs/operate/conversations.md {/* docs-visuals */} ## Read the unified inbox [#read-the-unified-inbox] Open a site's **Conversations** page to see the newest 30 threads across hosted/embedded web chat, voice messages, live-call transcripts, Telegram, WhatsApp, Messenger, Discord, and KakaoTalk. Use **Load more** to move backward by last activity time. Each row shows the channel, best available visitor identity, last-message preview, message count, and last activity. Open a row to read the complete stored transcript with timestamps. Assistant bubbles can display the knowledge source and section recorded for a grounded answer. The inbox is an owner review surface, not a channel reply composer. Follow up through an approved contact path rather than assuming every external connector can be answered from the dashboard. ## Understand identity boundaries [#understand-identity-boundaries] Web, live voice, and each connector use different visitor identifiers. A Telegram user, browser visitor, and WhatsApp number are separate identities unless you have explicit, consented linking data. Similar display names are not proof that two threads belong to the same person. Connector labels can identify the bot/Page/application rather than the visitor when the external platform does not provide a safe display identity. Treat channel IDs and inferred names as untrusted metadata, not authorization. ## Review answer quality [#review-answer-quality] For a representative thread: 1. Read the visitor's question in context rather than judging the last answer alone. 2. Confirm the response follows persona/language boundaries. 3. Inspect displayed grounding sources and compare them with the current knowledge version. 4. Note unanswered, repeated, misleading, or unnecessary follow-up questions. 5. Add a validated missing fact to knowledge/FAQ and test it in a new conversation. Historical messages do not rewrite when a knowledge source changes. Start a new thread for regression testing after an update. ## Capture and manage leads [#capture-and-manage-leads] Lead capture can store a visitor's name, email, and stated request when the agent flow permits it. Recent leads appear in **Analytics**, where an owner can generate/send an approved follow-up, export lead data, or delete an individual lead. Ask only for information needed for the declared purpose. A product question or chat message does not automatically create marketing consent. Before follow-up, confirm the channel, purpose, jurisdiction, and opt-out requirements that apply. Generated follow-up text is a draft. Review recipient, claims, links, pricing, tone, and personal data before sending. Never let the agent invent an offer or promise not present in canonical knowledge. ## Export and delete safely [#export-and-delete-safely] Lead export creates a downstream copy under your control. Store it only in approved systems, restrict access, and include it in retention/deletion procedures. Deleting a lead removes that lead record but may not erase a related conversation, provider message, prior export, notification, or legally retained billing record. Disconnecting an embed or connector also does not automatically erase its historical threads. Follow the account privacy workflow when a visitor makes a deletion/access request, and remove downstream copies too. ## Operational review routine [#operational-review-routine] * Daily during launch: inspect new threads, handoffs, provider failures, and high-risk answers. * Weekly: group repeated questions, identify knowledge gaps, and check lead follow-up status. * After content/model changes: rerun the acceptance set and inspect citations. * After an incident: preserve safe timestamps/IDs, contain the affected channel, and avoid circulating raw private transcripts. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ----------------------------- | ---------------------------------------------------------------------------------------------------- | | Expected thread is absent | Correct site, channel delivery, connector last event, public test completion, and activity time | | Thread opens with fewer turns | Whether the source was a live transcript, connector history, or a different visitor/channel identity | | Citation is missing | The answer may not have used retrievable knowledge or older records may lack citation metadata | | Same person appears twice | Expected across separate browser/channel identifiers; do not merge by name alone | | Follow-up unavailable | Lead email/contact, access state, send configuration, rate limits, and owner review requirements | --- # Monetization Source: /docs/operate/monetization.md {/* docs-visuals */} ## Know the two billing relationships [#know-the-two-billing-relationships] **Monetization** lets a site owner sell an offer to a visitor. This is separate from the owner's Make Agent Fast plan, credit packs, and custom-domain add-on. Creator revenue/order records and owner platform usage must be reconciled separately. The page displays its current checkout mode. **Polar (live payments)** can accept configured real purchases. **Local/dev (test mode)** is for development behavior and must never be represented as settled production payment. ## Create an offering [#create-an-offering] Choose one of the supported kinds: | Type | Intended use | | -------------------------- | ----------------------------------------- | | **Paid access** | Unlock a members-only agent experience | | **Paid call/consultation** | Sell a consultation or call outcome | | **Tip** | Fixed-price or pay-what-you-want support | | **Digital good** | Sell a defined digital deliverable/access | Enter a specific title, description, price in cents, currency, and optional Polar product key. The key maps the offering to a configured provider product. An active offering can be shown or recommended in the agent conversation; an inactive one remains owner-visible but should not be sold. Use one canonical source for price, fulfillment, availability, refund terms, tax treatment, and support. Add those approved facts to knowledge; the agent must not invent a discount, guarantee, delivery time, or scarcity claim. ## Gate the agent behind paid access [#gate-the-agent-behind-paid-access] Create and activate a **Paid access** offering first, then select it under **Members-only agent**. Visitors who have not unlocked it receive a purchase/unlock path instead of normal chat access. Only active access products can be selected; the runtime also fails open rather than permanently bricking chat if a selected offer disappears. Test logged-out/new-visitor, successful checkout, return access, canceled checkout, duplicate provider event, inactive offer, and refund/revocation behavior before launch. ## Configure checkout rails [#configure-checkout-rails] For live Polar checkout, the platform deployment and product mapping must be configured. An optional Korean-payment control can offer KakaoPay/Naver Pay/Toss for KRW offers only when the platform reports that rail as available; turning on an unavailable toggle does not create a provider integration. Use provider test mode where supported, then verify a small real purchase according to your operational policy. Confirm currency minor units: for USD, `2500` means `$25.00`. Do not guess cent behavior for a currency with different minor-unit conventions. ## Review orders and revenue [#review-orders-and-revenue] The page reports paid-order count, revenue rollup, and recent purchases. “Paid” reflects the recorded checkout/provider state; reconcile provider fees, taxes, refunds, chargebacks, payouts, and fulfillment in the appropriate financial system. Deleting an offering does not erase historical order or billing records. Deactivating is safer than deleting when an operator needs the existing configuration for investigation. ## Optional outcome pricing [#optional-outcome-pricing] **Pay per outcome** can deduct a fixed owner-wallet credit amount for a qualified lead or booked call in addition to normal usage. It is not visitor checkout and does not become creator revenue. Leave it off unless the owner understands the extra credit charge and the exact event definitions. ## Launch checklist [#launch-checklist] * Live/test checkout mode is unmistakable. * Offer title, description, currency, price, and provider product match. * Fulfillment, access duration, refunds, tax, support, and privacy are documented. * Agent knowledge contains only approved commercial claims. * Success, cancel, duplicate, refund, and inactive-offer paths are tested. * Purchase alerts, fulfillment owner, accounting reconciliation, and incident process are assigned. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------------- | -------------------------------------------------------------------------------- | | Offer does not appear | It is active, relevant to the conversation, and site state is current | | Live checkout unavailable | Deployment payment configuration and offering's provider product key | | Access remains locked after payment | Recorded purchase state, correct visitor/session, provider webhook, and offer ID | | Korean payment toggle has no effect | Platform rail is not configured/available or offer currency is not supported | | Revenue differs from payout | Provider fees, refunds, chargebacks, taxes, settlement timing, and currency | | Owner credits decrease after outcomes | Optional outcome pricing is enabled in addition to normal usage | --- # Notifications Source: /docs/operate/notifications.md {/* docs-visuals */} ## What appears here [#what-appears-here] The site's **Notifications** page is an in-dashboard event feed. It currently records three owner-facing event types: | Type | Meaning | | ----------- | ------------------------------------------------------- | | **Lead** | The agent captured a visitor contact/intention record | | **Handoff** | The conversation requested or triggered human follow-up | | **Booking** | A booking outcome was recorded | Each item can show the event type, creation time, visitor name/email when available, and a short excerpt. An unread item is visually highlighted and contributes to the unread count. This page is not a notification-preferences or external-delivery screen. Marking an item read does not email a visitor, reply to a connector, change a lead, or unsubscribe anyone from an external provider. ## Review and mark events [#review-and-mark-events] 1. Open **Notifications** for the intended site. 2. Prioritize handoffs and time-sensitive bookings. 3. Open the related **Conversations** or **Analytics** context when identity and intent need confirmation. 4. Select **Mark read** only after the responsible owner has triaged it. 5. Use **Mark all read** only when every visible/unread event has been intentionally reviewed. 6. Select **Load more** for older pages. Read state is an owner workflow aid, not proof that a visitor received follow-up. Track the actual follow-up in the approved system of record. ## Protect visitor data [#protect-visitor-data] Names, email addresses, booking context, and excerpts can contain personal data. Limit dashboard access, avoid copying events into broadly accessible chat rooms, and do not treat the excerpt as the complete consent/context record. Deleting or marking a notification does not automatically remove the related lead, conversation, booking, provider record, or exported copy. Use the privacy/deletion workflow for data-subject requests. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------------ | -------------------------------------------------------------------------------------------------- | | Expected event is absent | Correct site, actual lead/handoff/booking completion, event time, and related conversation | | Unread count seems stale | Refresh after the mark action and verify it reported success | | Marking read did not contact visitor | Expected; this feed does not send follow-up | | Older item is missing | Use **Load more**; the first page is intentionally bounded | | Need email/Slack preferences | Not configured from this page; use the platform's available external workflow/API/webhooks instead | For automation, consume the supported Public API resources or signed webhook events rather than scraping this dashboard. --- # Credits and usage Source: /docs/operate/usage.md {/* docs-visuals */} ## Read the usage page [#read-the-usage-page] The dashboard usage area combines billing context with the resources that can stop an operation: current plan, credit balance, site allowance, live-voice minutes, clone entitlement, top-up choices, and billing actions. Check it before a launch and after adding a high-volume embed, connector, or API client. Do not interpret one number as a universal quota. Each entitlement protects a different resource. ## Distinguish balances and limits [#distinguish-balances-and-limits] | Resource | Resets or expires | What happens at the limit | | ------------------ | ---------------------------------------------------------------- | -------------------------------------------------------------------------------- | | Monthly AI credits | Reset with the plan allowance; unused monthly allocation expires | Metered AI/knowledge/voice actions can be rejected | | Purchased credits | Do not expire under current rules | Continue to fund metered work while access remains active | | Site count | Plan entitlement | Creation of another site is blocked | | Live voice minutes | Monthly plan allowance | New or continuing real-time sessions are limited/ended | | Voice clones | Plan count/fair-use entitlement | Another first-time clone is blocked; replacing an existing site clone is allowed | | API requests | Per API key per minute | Request returns `429` with retry headers | | Custom domain | Separate $2/month add-on per site | Domain can suspend when its add-on or platform plan lapses | Credits do not buy additional API throughput, sites, live minutes, clones, or domains. An entitlement does not guarantee the credit/provider path required to complete the operation. ## What consumes credits [#what-consumes-credits] Metered work can include site/agent generation, normal agent answers, knowledge embeddings, media transcription, text-to-speech, connector voice, and voice cloning. Cost varies with provider, model, prompt/context length, output length, audio duration, and operation type. One “message” therefore has no fixed universal credit price. Owner preview/test actions can consume real resources when they call live providers. Use short, representative acceptance tests and avoid repeatedly indexing the same large source or cloning the same sample while debugging an unrelated issue. ## Read usage history [#read-usage-history] **Usage history** shows the final credits actually consumed by each completed operation. Temporary authorization holds, reserve settlements, and refunds remain internal accounting details instead of appearing as separate customer-facing rows. Very small embedding operations display as `<0.001` rather than being rounded to zero. **Other balance changes** is a separate table for plan-credit grants, top-ups, expiries, and manual adjustments. It does not duplicate normal AI usage. While an operation is still in progress, the spendable balance can temporarily reflect a hold before the final usage row appears or a failed operation is released; refresh after completion when reconciling a just-finished action. ## Monthly and purchased credits [#monthly-and-purchased-credits] The monthly allowance comes from the active plan and is designed for that billing period. Unused monthly allowance is not a long-term bank. One-time packs add 100 credits for $8, 500 for $35, or 1,500 for $90; purchased credits do not expire under current rules. Buying a pack does not restore a canceled/expired platform subscription or a lapsed custom-domain add-on. Resolve access/billing first when the dashboard reports an entitlement problem rather than only a low balance. ## Live voice accounting [#live-voice-accounting] Pro includes 30 live minutes per month and Studio includes 90. Each call also has an owner-configured automatic duration limit from 30 seconds to 20 minutes. When a live-call token is issued, the wallet is charged up front for the server-authorized maximum duration, bounded by the remaining monthly allowance. The same duration is held against the live-minute allowance so concurrent calls cannot exceed it. Live media flows directly between the browser and the real-time provider, so the server cannot independently verify a shorter client-reported duration. Unused time is therefore not partially refunded after a token is delivered; an abandoned or unreported session is charged at its authorized limit. A server-side failure before token delivery releases the charge. End calls cleanly to close the session promptly, but forecast credits using the configured call limit rather than the caller's expected talk time. Voice messages are different: they consume metered transcription/model/speech work rather than the live-minute entitlement. Diagnose the correct bucket when one mode succeeds and the other fails. ## Forecast usage [#forecast-usage] 1. Choose representative short, normal, and long visitor questions. 2. Test text, knowledge-heavy answers, voice messages, and live calls separately. 3. Record credit/minute changes for the acceptance set. 4. Multiply by expected daily traffic and channel mix. 5. Add headroom for retries, crawls, provider failures, internal QA, campaigns, and traffic spikes. 6. Recheck after changing model, provider, source size, response length, or voice behavior. Use analytics and conversations to find what caused growth. A traffic spike, looping API client, leaked key, copied embed on an unintended origin, connector retry, or repeated indexing job needs a different fix. ## Prevent unexpected usage [#prevent-unexpected-usage] * Restrict embeds to exact intended origins. * Give API keys the smallest scopes and separate keys by service/environment. * Make mutations idempotent and honor `429` retry headers. * Verify webhook signatures and deduplicate event IDs. * Rotate leaked API/provider/connector credentials immediately. * Keep public agents bounded by clear persona and response instructions. * Remove stale automation, preview deployments, connectors, and unused keys. * Alert operational owners before balances or live minutes approach zero. ## Investigate a spike [#investigate-a-spike] Record the affected workspace/site, time range, channel, deployment, and recent changes. Compare usage with analytics, conversation timestamps, API request IDs, webhook delivery logs, and connector last-event/error state. Do not send plaintext keys or visitor content to support unless an approved secure process explicitly requires it. Contain the source first: disable a connector, revoke a key, remove an unintended origin, or stop a client retry loop. Then preserve safe evidence, rotate credentials where needed, and re-enable one path at a time. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------------------ | ----------------------------------------------------------------------------------------- | | Out of credits despite unused live minutes | Separate resources; add/renew credits for normal AI work | | Voice messages work but live call does not | Plan live entitlement, remaining minutes, real-time key, and session state | | Top-up purchased but feature still locked | Packs add credits only; check plan/site/clone/domain entitlement | | API returns `429` with credits remaining | Per-key request rate; follow reset/retry headers | | Domain says payment required | Separate domain add-on or platform subscription status | | Balance falls unexpectedly | Recent embeds, connectors, API keys, indexing, voice/cloning, and provider/client retries | See [Plans and pricing](/docs/account/pricing) for the current entitlement table. --- # Custom domains Source: /docs/publish/custom-domains.md {/* docs-visuals */} A custom domain is a $2/month add-on for a site on an active paid plan. One site can have one custom-domain record at a time. The add-on is separate from AI credits, live voice minutes, and the platform plan subscription. ## Understand the lifecycle [#understand-the-lifecycle] | Status | Meaning | Your next action | | -------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------- | | **Pending DNS** | Domain record exists but ownership TXT was not found | Add the shown TXT record and select **Check DNS** | | **Verified** | Ownership is confirmed; no domain add-on is active yet | Start the add-on checkout | | **Attaching** | Payment is active and the platform is provisioning apex/`www` | Add every displayed DNS record and recheck | | **Active** | All provider hosts are active | Open HTTPS, choose the primary host, and publish the site | | **Error** | Provider attach or DNS activation failed | Read the saved error, fix records, and retry/check | | **Suspended / payment required** | Plan or domain add-on entitlement lapsed | Restore billing or remove the domain | The custom host serves only while the site is published and the platform subscription/domain entitlement is active. A valid DNS record alone does not bypass those checks. ## Choose the domain and primary host [#choose-the-domain-and-primary-host] Open the correct site's **Domain** page and enter the registrable domain, such as `example.com`. Do not enter a path, scheme, port, or unrelated platform hostname. Choose whether `www.example.com` or `example.com` (apex) should be primary; `www` is the recommended default for the broadest DNS-provider compatibility. The platform attaches both apex and `www` variants. The selected primary host is canonical, and the other variant redirects to it when active. You can switch the primary choice later without creating a second domain record. ## Verify ownership before payment [#verify-ownership-before-payment] After adding the domain, create the exact TXT record displayed in the dashboard: ```text Type: TXT Name: _maf-verify.example.com Value: ``` DNS providers differ in their **Name/Host** field: some automatically append `example.com`, so they may expect only `_maf-verify`. Follow the provider's convention and use a DNS lookup tool to confirm the final fully qualified record returns the exact token. Select **Check DNS**. Verification is free and happens before checkout. Do not publish the token for another domain or reuse it across sites. Pending verification records can expire after an extended incomplete setup, so finish the flow rather than treating it as a permanent reservation. ## Activate the add-on [#activate-the-add-on] When the status is **Verified**, select **Activate custom domain** and complete the Polar checkout. The add-on belongs to this site/domain. A verified but unpaid setup does not serve traffic. The platform checks provider capacity before selling the add-on. If capacity is temporarily unavailable, use the displayed request/waitlist action; do not pay for or point production traffic at an integration that cannot yet attach. After payment, the status becomes **Attaching** and the platform requests both hosts from the edge provider. The page refreshes while provisioning and shows the authoritative DNS records returned for your domain. ## Add activation DNS records [#add-activation-dns-records] Create every record exactly as shown. Depending on the DNS provider and host, these may be CNAME or other targets. Remove conflicting A, AAAA, or CNAME records for the same hostname. Do not copy a target from another customer's documentation because edge values can be deployment-specific. If a DNS proxy/CDN is enabled, start with DNS-only mode when it hides validation or changes TLS behavior; re-enable proxying only after you understand the resulting certificate and caching path. Select **Check DNS** until every host is active. The platform marks the domain **Active** only when all provider references report active; partial activation is not treated as success. ## Verify production behavior [#verify-production-behavior] 1. Publish the site. 2. Open `https://` for the primary host in a private window. 3. Confirm the alternate host redirects to the primary host. 4. Inspect the certificate hostname and browser security state. 5. Send an agent test message and complete key links/forms. 6. Confirm the original hosted slug redirects or identifies the custom domain as canonical where expected. 7. Add the custom host to external analytics, search, email, or provider allowlists that depend on hostname. The site's active custom domain is automatically allowed as its embed parent; you do not need to duplicate it in the embed-origin list for that same site. ## Billing, suspension, and removal [#billing-suspension-and-removal] If the domain add-on payment lapses, the domain can enter **Payment required**. If the overall platform plan lapses, it can be suspended even when DNS remains correct. Use the billing portal action to restore entitlement, then recheck activation. Removing the domain detaches both hosts, cancels the associated add-on, and returns the site to its hosted subdomain. It does not alter DNS records at your registrar. Remove or repurpose those records after the hosted fallback is confirmed. Keep the old destination working during a migration until the new host serves HTTPS. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------------- | -------------------------------------------------------------------------------------------------------- | | TXT never verifies | Final FQDN, provider auto-appended zone, exact token, propagation, and duplicate TXT values | | Stuck attaching | Every displayed record, conflicting A/AAAA/CNAME, proxy mode, provider status, and **Last checked** time | | Apex works but `www` fails | Both host records exist; the platform waits for both before full activation | | Browser shows certificate error | Do not share yet; wait for active state, correct DNS, and certificate issuance | | Domain is active but site unavailable | Site published state and active platform/domain billing entitlement | | Need a different domain | Remove the existing site domain first, confirm hosted fallback, then add/verify the replacement | | Status reports capacity | Use the request-access action; repeated DNS edits cannot fix provider capacity | DNS propagation has no universal instant completion time. Use the dashboard's exact records and last-checked state rather than guessing from one resolver. --- # Hosted sites Source: /docs/publish/hosted-sites.md {/* docs-visuals */} Every site has a hosted public experience. It is the fastest way to test the visitor journey, share an agent without writing code, and provide a reliable destination for platforms that do not allow JavaScript embeds. ## Choose a durable slug [#choose-a-durable-slug] The slug becomes the public address, normally `https://YOUR_SLUG.makeagent.fast`. Use a short name visitors recognize. A valid slug must not collide with another tenant or a reserved platform route such as `docs`, `dashboard`, or `api`. Treat the slug as public information, not a secret. Changing a public address after launch can break saved links, UTM campaigns, QR codes, and provider profiles, so settle on the durable name before distribution. Change the included subdomain from the site's **Domain** page. Use lowercase letters, numbers, and hyphens, with no leading or trailing hyphen, up to 40 characters. Saving a new slug makes the old hosted subdomain stop working immediately; the platform does not promise an automatic redirect from the old slug. An already active custom domain is unaffected, but links that still use the hosted address must be updated. ## Prepare the visitor experience [#prepare-the-visitor-experience] Before publishing, review more than the generated text: * Site title, description, image, sections, calls to action, contact details, and legal/privacy links. * Agent name, persona, language, fallback behavior, and knowledge acceptance questions. * Text, voice-message, and live-call surfaces that your plan enables. * Mobile navigation, long content, Korean/English text, keyboard focus, contrast, and form errors. * Lead-capture purpose and the operator who will review **Conversations**. Use an owner preview for quick iteration, but perform final acceptance on the public address in a private browser window. ## Publish and verify [#publish-and-verify] 1. Open the intended site and confirm its slug. 2. Save the latest agent, knowledge, appearance, and page changes. 3. Select **Publish**. 4. Open the public URL in a private window. 5. Send an acceptance-test question and complete every important call to action. 6. Check narrow mobile width and any enabled voice permission. 7. Confirm the test thread appears in **Conversations** and usage is attributed to the expected site. Publishing exposes the current site state. Later dashboard edits should be treated as a new draft/review cycle; publish again after the changes are ready for visitors. ## Search and sharing [#search-and-sharing] Published sites provide crawlable page metadata and social-sharing information. Use a specific title and description that explain the visitor outcome, plus a representative image you have the right to publish. Test the URL in the sharing channels that matter to you because social crawlers can cache an older preview. When an active custom domain is attached, it can become the canonical public address and the hosted slug can redirect to it. Select one primary host (`www` or apex) so search engines and visitors do not treat two hostnames as competing copies. ## Connect other surfaces [#connect-other-surfaces] The hosted site and an embed can use the same site agent. Messaging connectors also route into that agent but keep channel-scoped conversation identity. Publishing does not automatically install the embed or configure provider webhooks; complete those guides separately. Use the hosted URL as a normal button/link for Notion, Linktree, email signatures, QR codes, and other surfaces that cannot load arbitrary JavaScript. Add UTM parameters when you need campaign attribution, without putting personal data in the URL. ## Update or unpublish [#update-or-unpublish] To update a live site, make the change, test it in the owner flow, publish again, and verify the public URL in a private window. If an edge or social preview appears stale, distinguish the browser page from a third-party cached share card before changing the site again. Unpublishing removes the public site experience but does not delete the site, knowledge sources, agents, conversation history, custom-domain billing, or external provider configuration. Connected messaging bots may continue answering; disable/remove each connector separately. Remove a custom domain from its **Domain** page if its add-on should also stop. ## Launch checklist [#launch-checklist] * Public URL, title, metadata, favicon/image, and primary CTA are correct. * The agent answers known questions and declines or escalates unknown ones. * No draft-only, private, or credential-bearing content is visible. * Mobile, keyboard, form, and voice behavior has been tested. * Privacy, AI/synthetic-media disclosure, and consent language match the enabled features. * Conversation owner, usage monitoring, and incident contact are assigned. * Embed origins, connector provider modes, and custom-domain state are verified independently. ## Troubleshooting [#troubleshooting] | Symptom | Check | | ------------------------------------------- | ----------------------------------------------------------------------------- | | Public URL returns unavailable/not found | Exact slug, published state, active account access, and requested host | | Dashboard change is absent | Correct site, saved action result, republished state, and private-window test | | Old sharing image remains | Social platform cache/debugger rather than the live page HTML | | Custom domain fails but slug works | Domain status, DNS records, add-on billing, primary host, and TLS | | Connector answers while site is unpublished | Expected; disable the connector separately if all channel access must stop |