Connect your own form
Auto-trigger a phone call when your existing lead form is submitted.
You already have a lead form. You do not have to replace it β have it trigger a phone call as well, so a submitted form rings your team within seconds instead of sitting in an inbox.
This is the highest-value integration the widget has. Speed of response is the single biggest driver of inbound lead conversion, and a form that rings you immediately beats one that emails you every time.
Two ways
The widget is installed. Shortest path, and it handles validation for you.
You want the widget's own confirmation screen and ringing state.
You want your own interface end to end, or you are submitting from a server.
Ask the widget
If the widget is already on the page, requestCallback() does the whole thing and tells you what happened:
No key in your code, no endpoint to get right, and the phone number is validated before anything leaves the browser. It resolves { ok, id, message } and never rejects, so there is nothing to catch.
Hand off to the widget
To show the widget's own confirmation and ringing state instead of your own, pass the number to call() and let it take over:
call() returns a boolean that tells you nothing about the outcome β listen for callback:requested and callback:failed, or use requestCallback() above.
Pair either with hideLauncher() if you do not want the floating button on the page.
Post directly
To keep your own interface entirely, post to the widget's public endpoint. This uses your widget key β the publishable one from your snippet's data-key β not a secret API key.
Use the same host as your snippet's data-api value.
Fields
phone
yes
The number to call back, international format.
name
no
Shown to whoever takes the call.
email
no
Stored on the resulting lead.
department
no
Routes to a specific team.
captured
no
Extra fields you collected, carried onto the lead.
sessionId
no
Ties the request to a widget session for attribution. If the widget is on the page, get it from window.LimeCall.getVisitorId().
Success returns the callback's id. A failure returns a message worth showing the visitor β it is where "outside business hours" arrives.
Scheduling instead
Same shape plus a time, to a different path:
Worked example β an existing lead form
A complete drop-in: keep your form and its styling, add a call request, and fall back gracefully when nobody can be reached.
What that example gets right
Your backend still receives the lead. The call request is added alongside, not instead. If LimeCall is unreachable you have still captured the enquiry.
It tells the visitor a call is coming. Someone who does not expect a call does not answer an unknown number, and the whole point is lost. Say it before the phone rings.
It shows the API's own message on failure. "We're closed right now" is useful; "Something went wrong" is not.
It disables the button. A visitor who clicks twice otherwise gets rung twice.
captured carries the free-text field onto the lead, so whoever picks up can see what was asked before they speak.
Only promise a call someone can take
Ask before you offer. hasAvailableAgents() accounts for working hours, whether the people the widget routes to still resolve, and β on an AI-routed widget β whether you have minutes left:
Changing the label is better than hiding the option. The lead is still captured either way; the visitor just is not told to expect a ringing phone that never comes.
If you are posting directly rather than using the widget, the request still succeeds outside hours β the response message is where "we're closed right now" arrives, which is why the example above shows it to the visitor verbatim.
Which key is which
The widget key (data-key) is publishable β it is already in your page source and belongs in browser code. A secret key (sk_live_β¦) is not: putting one in front-end code exposes your whole account to anyone who opens developer tools. The widget endpoints take X-Widget-Key; the REST API takes Authorization: Bearer. They are not interchangeable.
See API keys.
Doing it server-side instead
If your form already posts to your own backend, create the callback there. It is the right choice when you want to validate, deduplicate, enrich or rate-limit before spending a call β and the visitor's browser never has to be trusted.
It is the same endpoint, with a secret key instead of a widget key:
Header
X-Widget-Key: pk_live_β¦
Authorization: Bearer sk_live_β¦
Origin
Must be on your allowed list
Not checked
fromNumber
From your saved widget settings
Required in the body
Daily cap
Your widget's cap (default 50)
dailyCallbackCap, default 200
The secret key needs the calls:write scope. Without it the request is rejected with 403 insufficient_scope β placing a call spends real money, so it is held to the same scope requirement as every /api/v1 route.
Node
PHP
What you can send
phone and fromNumber are required. name, email, department, captured and sessionId behave exactly as they do from the browser.
The server path also accepts routing overrides the browser cannot set, so one key can serve several brands or campaigns: dailyCallbackCap, bridgeProvider ("vapi" or "grok"), voiceAgentId, callableCountries, blockedCountries and blockVoip.
Errors worth handling
Status
error
Means
400
missing_from_number
fromNumber was not sent.
401
unauthorized
Key missing, malformed or revoked.
403
insufficient_scope
The key lacks calls:write.
403
from_not_owned
That caller ID is not a number on your account.
403
(destination)
The number is in a range we refuse to dial.
429
spend_cap
The daily cap is spent.
See API keys for creating a key with the right scope.
Was this helpful?