The short answer: open your project in Cursor, save the prompt from vistaraai.in/integration/cursor as VISTARA.md, and in Agent mode ask: "Follow @VISTARA.md to add Vistara AI voice calling to this project." Cursor checks your API key, helps you build and test a voice agent with a live call to your phone, then edits your project so it can start calls and receive their results by webhook.
Agent mode or Ask mode?
| Mode | What Cursor does |
|---|---|
| Agent | Runs the Vistara API calls in the terminal, reads the responses and edits files across the project. Recommended. |
| Ask | Explains and gives you the commands and code to apply yourself; you paste the responses back. |
Set up in two minutes
- Create a Vistara AI account at app.vistaraai.in (₹150 free credit with a GSTIN, ₹50 without — up to 200 calls, since unanswered calls are never charged).
- Copy your API key from the Connect page and keep it in the environment as
VISTARA_API_KEY— exported in the terminal Cursor uses, or in a.envfile listed in.gitignore. - Download the prompt from the Cursor page and save it in the project root as
VISTARA.md, so you can reference it with@VISTARA.md.
The flow inside Cursor
- Key check. Cursor lists your active agents as a table — a successful response proves the key works.
- The agent. Pick an existing agent, improve one, or build a new one. Cursor asks about your use case, writes the agent prompt with Vistara's Voice Agent Builder, saves it in the repository and creates the agent after you approve it.
- Test call. Cursor confirms your number, places a test call and, once you hang up, shows the summary, extracted answers, transcript and recording. Ask for changes and test again.
- Code. Cursor reads your existing code first and puts the integration where similar integrations already live. Review its diffs like any pull request before accepting them.
What the webhook handler looks like
For an Express project, the endpoint Cursor writes usually follows this shape:
app.post('/webhooks/vistara', express.json({ limit: '2mb' }), async (req, res) => {
if (!tokenOk(req.query.token)) return res.sendStatus(401);
const { event, callId, data } = req.body || {};
if (!callId || !event) return res.sendStatus(400);
const isNew = await saveCallEvent({ callId, event, data, raw: req.body });
res.sendStatus(200);
if (isNew) queue.add('vistara-call-result', { callId });
});
Each line has a reason. Vistara webhooks are not signed, so the secret token in the URL is what proves the request is genuine. The same event can arrive more than once, so results are saved by callId and duplicates are ignored. Vistara retries anything that isn't a quick 2xx, so the handler answers first and does the slow work — CRM updates, notifications, redials — in a background job.
Mapping call results into your product
The data points you define on the agent (Vistara calls them deliverables) come back in data.answers, keyed by each question's exact text — for example "Was customer interested in demo?": "Yes". Ask Cursor to map each one to a field in your own data model. Two things to remember: renaming a question changes its key, and a call with meaningfulConversation: false (picked up and dropped, or "call me later") should be treated like an unanswered call when you decide whether to redial.
Mistakes Cursor will steer you away from
- Calling Vistara from browser code — the key would be exposed. Calls always go through your backend.
- Retrying "start call" after a timeout — the customer could get two calls.
- Assuming webhooks arrive exactly once and in order.
- Registering a localhost webhook URL — use a tunnelling tool during development.