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
userEmailis 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.
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"
}'curl -X POST https://api.everycallhandled.com/api/v1/apps/$APP_ID/publish \
-H 'Authorization: Bearer YOUR_TOKEN' \
-d '{ "subdomain": "harbour-hotel" }'{
"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 }.
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/unpublish' \
-H 'Authorization: Bearer YOUR_TOKEN'{ "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.
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"}'curl -X DELETE 'https://api.everycallhandled.com/api/v1/apps/{appId}/phone-number' \
-H 'Authorization: Bearer YOUR_TOKEN'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.
curl 'https://api.everycallhandled.com/api/v1/apps/{appId}/sip' \
-H 'Authorization: Bearer YOUR_TOKEN'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.
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"}'Requires edit_settings. Tears down the app's
inbound-calling route; optionally releases the platform-managed number.
Idempotent.
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/sip/disable' \
-H 'Authorization: Bearer YOUR_TOKEN'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://..." }
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"}'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.
curl 'https://api.everycallhandled.com/api/v1/apps/{appId}/embed-code' \
-H 'Authorization: Bearer YOUR_TOKEN'{
"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.
curl -X POST 'https://api.everycallhandled.com/api/v1/apps/{appId}/ask-page' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' -d '{}'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.
{
"id": "sms-{appId}-{hash}-s3",
"source": "sms",
"callerIdPhone": "+447700900123",
"description": "Text message conversation",
"status": "In Progress"
}