Guides

Testing without a phone

Run the whole call lifecycle with the mock engine and mock telephony, script the caller, and read the development outboxes.

You can build and test almost everything without dialling anyone. Two independent switches make this possible, and both default to the safe setting:

SwitchMock valueWhat it means
EnginemockA scripted simulation plays both sides of the call. No audio, no models
Telephony modemockNumber search, buy and release are simulated, and nothing is dialled at a carrier

The telephony mode is an operator setting. The engine is yours to choose: set agent_config.engine, or agent_data.engine on one call, to mock. If neither is set, the operator's default applies, and the shipped default is mock.

A first mock call

bash
curl -X POST https://api.kshyamai.space/call \
  -H "Authorization: Bearer vk-0123456789abcdef0123456789abcdef" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "3c90c3cc-0d44-4b50-8822-8dd25736052a",
    "recipient_phone_number": "+919876500123",
    "user_data": {"name": "Asha"},
    "agent_data": {"engine": "mock"}
  }'

The call goes through the real lifecycle: queued, initiated, ringing, in-progress, call-disconnected, completed. Webhooks fire for each status. At the end the execution has a transcript, an itemised cost, a recording, and extraction results, and the wallet is charged at the price book's rates.

Mock calls run faster than real time; the speed is an operator setting.

By default the caller says three fixed lines:

text
Hello, yes, who is this?
Okay, tell me more.
Sounds good. Thank you, bye!

Magic recipient numbers

The last four digits of the recipient number choose the outcome.

Number ends inOutcome
0000no-answer
1111busy
2222failed, with error_message Mock telephony failure
3333An answering machine
Anything elseA conversation, ending completed

For 3333, the machine is detected (unless task_config.voicemail is false), the call ends with answered_by_voice_mail: true and hangup reason Voicemail detected. With voicemail: false the agent talks to the machine and hangs up after hangup_after_silence.

Script the caller

agent_data.mock_script replaces the caller's lines.

json
{
  "agent_id": "3c90c3cc-0d44-4b50-8822-8dd25736052a",
  "recipient_phone_number": "+919876500123",
  "agent_data": {
    "engine": "mock",
    "mock_script": ["Hi, I want to check my order status", "It is ORD-1", "Thanks, bye"]
  }
}

For graph agents, a line event:<name> injects an external event and takes the current node's matching event edge. It is not a caller turn.

Script tool calls

Without instructions, a caller line triggers the first tool whose name is said in it, else the first whose description shares a keyword of four or more letters with it. To be exact, use agent_data.mock_tool_calls:

json
{"agent_data": {
  "engine": "mock",
  "mock_script": ["Where is my order?", "Thanks, bye"],
  "mock_tool_calls": [{"turn": 1, "name": "check_order_status", "arguments": {"order_id": "ORD-1"}}]
}}

turn is the caller line the tool fires on, counted from 1. When mock_tool_calls is present, only those tools fire.

A custom function on a mock call makes its real HTTP request to your API, with the same private-network check and 10 second timeout as on a live call. The agent then says <tool> returned: <body>, or Sorry, I could not complete <tool>: HTTP 500. A transfer tool fills transfer_call_data and ends the call; end_call ends it.

What the mock engine does and does not do

It honours:

  • {name} and {{name}} variables in the welcome message and graph prompts.
  • Knowledge bases: a reply that quotes the best-matching sentence, prefixed From our documents:.
  • Graph routing by the deterministic keyword rule, with node_path and node_extracted_data on the execution.
  • welcome_message_delay, call_hangup_message (said when the caller says "bye"), voicemail detection.
  • auto_reschedule: a line such as "call me back in 30 minutes", "tomorrow" or "at 6 pm" ends the call and creates a new scheduled call. The two executions point at each other in context_details.
  • The send_sms tool, through the compliance checks and the message provider.

It only logs, without acting on them: the ambient noise track, noise cancellation level, keypad setting, and the transcriber's keywords.

It never uses an LLM: replies are placeholders, and a graph's LLM nodes say [<node_id>] <prompt>. It tests your wiring, not the quality of the conversation.

Test chat

For a quick text conversation with the agent's real prompt, tools and knowledge base, use POST /agent-chat/{agent_id}; see Agents. With LLM credentials it answers with the agent's own model. Without, it follows the same rules as the mock engine. It accepts mock_tool_calls too.

To hear the agent, use a browser voice test. That one needs the real engine.

The mock recording

A mock call has a real, playable WAV file at telephony_data.recording_url: one tone per turn, higher for the agent and lower for the caller, each as long as the turn. It contains no speech.

Mock telephony and other development defaults

With the shipped defaults:

  • GET /phone-numbers/search returns made-up numbers, and POST /phone-numbers/buy always succeeds. The wallet is still debited.
  • The per-number daily cap is not applied.
  • POST /phone-numbers/verified returns the code as mock_otp instead of sending an SMS, while the message provider is the mock one.
  • Top-ups with the mock payment provider are paid at once.

Development outboxes

When the operator has not configured real email or SMS, messages are stored instead of sent.

bash
curl "https://api.kshyamai.space/dev/outbox?kind=invite&limit=5" \
  -H "Authorization: Bearer vs-0123456789abcdef0123456789abcdef"
json
[{"id": "8e7d6c5b-4a39-4281-9706-f5e4d3c2b1a0", "to": "ravi@example.test", "subject": "...", "kind": "invite",
  "text": "...", "html": "...", "link": "https://app.kshyamai.space/accept-invite?token=...",
  "created_at": "2026-10-13T12:30:00Z", "sent_at": null}]
  • GET /dev/outbox returns stored emails with the first link extracted. You see mail addressed to your own login; admins also see invitations sent into their workspace.
  • GET /dev/messages returns stored SMS and WhatsApp messages with their full text (admin). With the mock message provider, numbers ending 0000 fail.

GET /dev/outbox answers 404 in live telephony mode, and GET /dev/messages in production or live mode.

Limitations

  • The mock engine does not exercise speech recognition, the LLM, the voice, barge-in or latency.
  • Its transcripts are not passed through PII redaction.
  • Mock calls are charged like real ones. Use a test account, or expect the wallet to move.
  • If the operator runs live telephony, an agent on the own engine dials for real. Check which mode you are in before testing with real numbers.