Skip to content
Announcements5 min read

Voice calls: the same agent, now a phone call away

Your Keiki agent can now answer phone calls on its own number, with the same prompt, tools and rules as your text channels. Here's who it's for, how to get early access and what changes for SDK tools.

Grainy film still of a pale stone staircase climbing between orange-lit flowering hedges and statues toward a colonnade under a starry cobalt sky

Short answer: Voice calls give a Keiki agent its own phone number, so people can call the agent they already text and get the same answers, tools and bookings by voice. They're in early access: request access from Channels, Add channel, Phone calls, and your existing SDK tools work on calls without changes.

A guest texts a hotel's agent about a room for the weekend. The agent answers in seconds, but the guest still has a question about the view, and some people would rather just call. So they tap the contact, tap call, and the same agent picks up. "Friday to Sunday, ocean view?" "Booked. Room four-oh-seven." The confirmation lands on the hotel's site before they hang up.

Voice calls give your Keiki agent a phone number people can call. It's the same agent that answers your texts, with the same prompt, tools and rules. Voice calls are in early access today.

Why this matters

Text is the right channel most of the time, but not all of the time. Someone driving, someone who wants to talk it through, someone who just doesn't type: they call. Until now, that call went to a different system. A phone tree, a separate voice bot with its own script, or a person who has to look everything up again.

With voice calls, there's one agent behind every channel. What your agent can do over text, it can do on a call, because a call is another channel into the same agent, like iMessage, WhatsApp or Slack. You don't rebuild your tools for voice or keep two prompts in sync. When you change the agent, its calls change too.

  • One agent, every channel. Calls run the same agent turn as messages: same model settings, same tools and the same policies.
  • Natural turn-taking. Keiki connects the call to a real-time voice provider, so the caller can interrupt and the agent answers as they speak.
  • A voice that fits your brand. Pick a voice, a tone (friendly, professional, calm or upbeat) and one of 13 languages, and preview it before your first call.
  • Your own number. Keiki buys a local number in the area code you choose and connects it to your agent.

Who can use it

Voice calls are for any team whose customers sometimes want to talk: hotels and restaurants taking bookings, clinics confirming appointments, shops answering "is it in stock?", and support teams that already run their agent on Keiki. If your agent texts today, it can take calls.

Voice calls are in early access while we bring on teams one at a time. Your agent, its tools and its prompt don't need to change to use them.

How to get started

  1. Open the Keiki dashboard and go to Channels → Add channel → Phone calls. You can also start from the Channels section of an agent, including a draft.
  2. Tell us what the line is for and how many calls you expect each month. We'll follow up to turn it on for your account.
  3. Once it's on, choose the agent and an area code. Keiki buys the number and connects it.
  4. Choose a voice, a tone and a language, preview the greeting, then call the number.

For SDK users

If you build your agent with the SDK, there's nothing new to install. Your existing tools already work on calls. Every tool call carries the channel it came from in ctx.channel, so a tool can tell a call apart from a text when it matters. On a call, ctx.channel.channel is 'phone_call', and ctx.channel.caller is the caller's number in E.164 format.

Here's a booking tool that finds the guest by the number they're calling from, and keeps its answer short when it will be spoken:

TypeScript
import { Effect, Schema } from 'effect'
import { defineTool } from '@onkeiki/agents'

export const bookRoom = defineTool({
  name: 'book_room',
  description: 'Book a room for the guest. Confirm the dates and room type first.',
  input: Schema.Struct({ checkIn: Schema.String, checkOut: Schema.String, roomType: Schema.String }),
  execute: (input, ctx) =>
    Effect.promise(async () => {
      const onCall = ctx.channel?.channel === 'phone_call'
      const guestPhone = onCall ? ctx.channel?.caller : ctx.phone
      const booking = await reservations.create({ ...input, guestPhone })
      return onCall
        ? { confirmed: true, room: booking.room }
        : { confirmed: true, room: booking.room, confirmation: booking.code, url: booking.url }
    }),
})

reservations stands in for your own booking API. Add the tool to your agent the same way as any other, then sync:

TypeScript
import { create } from '@onkeiki/agents'
import { bookRoom } from './tools/book-room'

const kit = create({
  apiKey: process.env.KEIKI_API_KEY!,
  name: 'Northstar Coast',
  agent: {
    model: 'google/gemini-3.5-flash',
    systemPrompt: () => [
      'You are the front desk for Northstar Coast, a hotel by the sea.',
      'On a phone call, answer in one or two short sentences and read numbers out clearly.',
    ].join('\n'),
    tools: [bookRoom],
  },
})

await kit.sync()

A few things work differently on a call:

  • Approvals that need proof don't run on a call. A tool with approvalProof: 'verified' needs a Face ID card, a Slack button or the dashboard, and a call has none of those, so by default the tool doesn't run and the agent says so. Use Keiki Vault for those on iMessage.
  • Past threads aren't searchable on a call. The conversation_search and conversation_read tools are turned off for calls.
  • Keep spoken answers short. A reply that reads well in a bubble can be long out loud. Tell the agent in its prompt how to speak on a call.

What's next

Request early access from Channels → Add channel → Phone calls, or read Building iMessage Agents to see how one agent runs across every channel.