Checking your session…

Go Live — Phone Numbers & Web Chat

Your assistant exists as a draft until you take it live. Going live is the moment it becomes real: it gets a web address, can take a phone number, and starts answering. This chapter covers the full go-live surface — taking an assistant live and offline, buying and wiring a phone number, and putting the chat widget on your own website.

Requires publish.

To apply later changes, the app already has a subdomain; reuse it (only subdomain is required):

Response (200):

What going live does:

  • First time — takes the app live: gives it its web address, applies your configuration (prompt, tools, voice, billing, calling setup), wires phone routing, and sends a welcome email if userEmail is provided.
  • Afterwards — applies your draft changes to the live assistant. The subdomain and web address stay the same, and running balances and usage history are never touched — this changes behaviour, not billing state.

Going live completes within a few seconds. Until you take the assistant live, edits made via PATCH /apps/{appId} have no effect on what callers experience; GET /apps/{appId} tells you whether changes are waiting via hasPendingPublish.

POST/api/v1/apps/{appId}/publishpublish
curl -X POST https://api.everycallhandled.com/api/v1/apps/$APP_ID/publish \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "subdomain": "harbour-hotel",
    "userEmail": "owner@harbourhotel.co.uk",
    "userPhone": "+447700900000"
  }'
Sign in to run this against your account.
curl -X POST https://api.everycallhandled.com/api/v1/apps/$APP_ID/publish \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{ "subdomain": "harbour-hotel" }'
Sign in to run this against your account.
JSON
{
  "appId": "...",
  "subdomain": "harbour-hotel",
  "deploymentUrl": "https://harbour-hotel.app.everycallhandled.com",
  "embedCode": "<script ...></script>",
  "status": "published",
  "publishedAt": "2026-05-10T10:30:00Z"
}

Requires publish. Takes the live site offline immediately without deleting anything: the app, its knowledge, its conversation history and any phone number are all kept, and the subdomain is reserved so it can go live again on the same address. Conversations captured while live are hidden from listings until the app goes live again.

Response (200):

Calling it on an app that is not live returns { "success": true, "alreadyUnpublished": true }.

POST/api/v1/apps/{appId}/unpublishpublish
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/unpublish' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Sign in to run this against your account.
JSON
{ "success": true, "unpublished": true, "siteRemoved": true, "subdomainKept": "harbour-hotel" }

The fastest route to a phone-answering assistant: one call buys a number and wires it to the app.

Requires edit_settings.

POST searches and purchases a phone number for the requested country (GB regulatory requirements handled for you) and wires inbound calling in one call — one app, one number. A wiring failure automatically releases the just-purchased number and clears the app, so a paid number can never leak. DELETE unwires calling, releases the number (released numbers cannot be recovered), and clears it off the app.

POST/api/v1/apps/{appId}/phone-number# buy a number + wire calls: { country?: 'GB' }edit_settings
DELETE/api/v1/apps/{appId}/phone-number# unwire + release the number (permanent)edit_settings
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/phone-number' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"country":"GB"}'
Sign in to run this against your account.
curl -X DELETE 'https://api.everycallhandled.com/api/v1/apps/{appId}/phone-number' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Copy-only. DELETE is destructive — run it from your own terminal with the sample above, not from the docs.

Available to any signed-in member of the account that owns the app. Returns the app's current inbound-calling setup — whether calling is enabled, which number is wired, and the state of the underlying telephony resources.

GET/api/v1/apps/{appId}/sip
curl 'https://api.everycallhandled.com/api/v1/apps/{appId}/sip' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Sign in to run this against your account.

If you manage your own numbers (for example, bringing an existing number or your own Twilio account — see Credentials), the SIP endpoints give you direct control of the wiring that the phone-number endpoint above automates.

Requires edit_settings. Provisions the app's inbound-calling route; if twilioManaged="true", also provisions a platform-managed number. Idempotent.

POST/api/v1/apps/{appId}/sip/enableedit_settings
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/sip/enable' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"twilioManaged":"true"}'
Sign in to run this against your account.

Requires edit_settings. Tears down the app's inbound-calling route; optionally releases the platform-managed number. Idempotent.

POST/api/v1/apps/{appId}/sip/disableedit_settings
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/sip/disable' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Sign in to run this against your account.

Requires edit_settings. Sets (or clears) the Voice URL on the app's existing Twilio number, so an incoming call routes to this assistant — for when you keep the number in your own Twilio account. The app must already have a twilioPhoneNumber configured (otherwise 400).

Body: { "voiceUrl": "https://..." } — pass null to clear.

Response (200): { "success": true, "phoneNumber": "+44...", "voiceUrl": "https://..." }

POST/api/v1/apps/{appId}/voice/twilio-urledit_settings
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/voice/twilio-url' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"voiceUrl":"https://example.com/your-twilio-webhook"}'
Sign in to run this against your account.

Available to any signed-in member of the account that owns the app. Returns ready-to-paste iframe + <script> snippets for embedding the assistant on your own website. The app must be published first (otherwise 400).

Response (200):

The snippets themselves are intentionally shareable. Per-page embed options (start language, opening intent) are covered under Web-chat conversation config.

GET/api/v1/apps/{appId}/embed-code
curl 'https://api.everycallhandled.com/api/v1/apps/{appId}/embed-code' \
  -H 'Authorization: Bearer YOUR_TOKEN'
Sign in to run this against your account.
JSON
{
  "appId": "...",
  "appName": "...",
  "isPublished": true,
  "publishedUrl": "https://...",
  "embedCode": {
    "scriptTag": "<script src=\"https://...\"></script>",
    "iframe": "<iframe src=\"https://...\" ...></iframe>"
  },
  "urls": { "embedScript": "...", "widget": "...", "documentation": "..." },
  "configuration": {
    "position": ["bottom-right", "bottom-left"],
    "theme": ["auto", "light", "dark"],
    "mode": ["fab", "embedded"]
  }
}

Some businesses have no website to embed into — or want a chat entry that lives in the physical world: a card at reception, a sticker on a product, a page in a holiday-let welcome book. The ask page is a hosted page that is nothing but your assistant, ready for a QR.

Requires edit_settings. Returns { "url": "…", "key": "…" }. The call is get-or-create: an app has one ask page, and repeated calls return the same one. In the dashboard, the web-chat settings section has a Create an ask page button that does this and shows the URL with a printable QR.

POST/api/v1/apps/{appId}/ask-pageedit_settings
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/ask-page' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' -d '{}'
Sign in to run this against your account.

Your assistant answers text messages and WhatsApp on the same brain as the phone and the web, and the channel is opt-in per app. Turn it on under voiceChatTools.sms.enabled or voiceChatTools.whatsapp.enabled, with the number customers write to in phoneNumber on the same block. A UK landline cannot receive texts, so an app usually carries a separate mobile for SMS alongside its voice number.

Text is not a narrow web chat, and the assistant is told so. It writes the way a person texts — short, plain, no headings or bullet lists, choices in a sentence — and it never refers to "this chat window" or a page. It also knows the number the customer is writing from, so it never asks for a contact number it already has, and uses it for bookings and callbacks unless the customer offers a different one. On SMS the assistant answers in words rather than cards or pickers, because there is no screen to show them on; WhatsApp keeps them, where they arrive as native tappable lists.

One conversation per occasion. A text thread has no beginning or end at the network level, so the platform gives it one. After fifteen minutes of quiet the conversation closes and the next message starts a new one, which carries a short summary of the last so the customer is not met as a stranger. Each conversation is its own record with its own summary and its own notification, so your Conversations list reads one row per occasion rather than one endless row per customer. A conversation appears in the list the moment it starts, carrying its channel and the customer's number:

source is sms or whatsapp. The id keeps the customer's thread as its prefix and gains -s2, -s3 and so on for later conversations, so one person's conversations group by prefix or by callerIdPhone without any extra field. A conversation a team member has taken over does not roll while they are handling it.

The keywords carriers expect. These are answered by the platform and never reach the assistant:

The customer texts What happens
HELP or INFO One fixed line: who this is, the number to ring instead, that STOP opts out, and that message rates may apply. Composed from the app's name and the number they texted, so there is nothing to configure
STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT Silence on the wire, and the customer's record is stamped consents.reminders.sms = false
START or UNSTOP The opt-out is lifted and confirmed once

Each is matched as the whole message only. "Can you help me with my booking" is a customer talking to your receptionist, not a request for programme information.

The opt-out is honoured on our side, not only the carrier's. This matters more than it looks. A carrier-level block applies to the pair of numbers that saw the STOP, and reminders usually go out from a different number than the one a customer texts. So the platform records the opt-out on the customer's record, and every outbound text path consults it: the reminder sweep skips that customer and writes skipped:opted-out against the rule, which you can see in the booking's commsSent markers and in the send log. An opt-out from texts is not an opt-out from email or phone reminders, and marketing consent is left exactly as it was.


GET/api/v1/apps/{appId}/conversations
JSON
{
  "id": "sms-{appId}-{hash}-s3",
  "source": "sms",
  "callerIdPhone": "+447700900123",
  "description": "Text message conversation",
  "status": "In Progress"
}