Skip to content
Announcements5 min read

Introducing A2A on Keiki: Agent2Agent for your agents

Your Keiki agent now speaks A2A. Turn it on and any Agent2Agent client can find it by its Agent Card and call it, with the same tools and memory.

Grainy night photograph of a hand in a blue ruffled sleeve reaching toward a blur of white daisies, with a thin streak of blue light between them and the title Introducing A2A at the bottom right

Your support agent knows your orders, your policies and your customers. Then a partner's agent needs to ask it something. A procurement agent wants a delivery date, or a travel agent wants to change a booking. Until now the choice was a custom integration for each caller, or leaving their agent out.

Starting today, every Keiki agent can speak A2A, the open Agent2Agent protocol. Turn it on, and any A2A client finds your agent by its Agent Card and sends it messages. That covers the official SDKs and agents from other vendors. Your agent answers with the model, instructions, tools and memory it already uses on iMessage, SMS, WhatsApp, Slack and email. You don't need a new deployment or a second agent.

What is the A2A protocol?

The Agent2Agent (A2A) protocol is an open standard that lets AI agents talk to each other, even when different vendors built them on different frameworks. An agent describes itself in an Agent Card, a JSON file at a well-known URL. The card lists its name, its skills, where to send messages and how to authenticate. A client reads the card, then sends messages over JSON-RPC. Each message becomes a task, and the task ends with the agent's reply.

Agents call each other as black boxes. The caller never sees your prompt, your tools or your customer data. It sees only the answer your agent chooses to give.

A2A vs MCP

The two protocols do different jobs. MCP connects an agent to tools and data. A2A connects an agent to other agents. On Keiki they work together. Your agent calls tools over MCP inside its turn, and other agents reach it over A2A.

MCPA2A
ConnectsAn agent to tools and dataAn agent to other agents
The other side isA server of functionsAn agent with its own reasoning
You get backA tool resultThe agent's reply, as a task

What you get

  • An Agent Card for every agent. Keiki publishes the card for you at https://onkeiki.com/a2a/<agent id>/.well-known/agent-card.json. It carries your agent's name, its description and a chat skill. The address uses the agent's id, so renaming the agent never breaks a saved URL.
  • One agent, every protocol. An A2A message is one ordinary turn of your agent. It uses the same model, tools and memory as a text from a customer.
  • A2A v1.0, plus v0.3 for older clients. Keiki answers both from the same address. A client that sends A2A-Version: 0.3, or sends no version header, gets the v0.3 card and wire format.
  • Streaming. SendStreamingMessage streams the reply as it's written. While your agent is busy with a long tool call, Keiki sends an SSE comment every 15 seconds, so proxies don't close the stream.
  • Conversations that remember. Messages that share a contextId are one conversation, so your agent remembers what the caller said three turns ago.
  • Traces and billing you already have. Each A2A conversation is stored, traced and billed like one that arrived over SMS. You debug it in the same trace view.

How to turn on A2A for a Keiki agent

  1. Turn on the channel. Open Agents, pick your agent, go to Channels, and select Turn on on the A2A row. The row then shows the Agent Card URL with a copy button. While the channel is off, the agent has no card and every call is refused.

    If you deploy from code, add the channel to sync instead:

    import { createCloudClient } from '@onkeiki/agents/cloud'
    
    const cloud = createCloudClient({ apiKey: process.env.KEIKI_API_KEY! })
    const deployment = await cloud.sync({
      externalRef: 'support',
      agent: { name: 'Support', model: '<model-id>', system_prompt: 'You answer order questions.' },
      tools: [],
      channels: [{ type: 'a2a' }],
    })
    const cardUrl = `https://onkeiki.com/a2a/${deployment.agent.id}/.well-known/agent-card.json`
  2. Give the caller an API key. Callers authenticate with one of your organization's developer API keys, sent as a bearer token. Create one under Settings → API Keys. If you scope the key to a single agent, its holder can call only that agent. Revoke the key and its A2A access ends.

  3. Call your agent from any A2A client. Here's the official JavaScript SDK sending a message:

    import { Role } from '@a2a-js/sdk'
    import { ClientFactory, DefaultAgentCardResolver, JsonRpcTransportFactory, createAuthenticatingFetchWithRetry } from '@a2a-js/sdk/client'
    
    const authFetch = createAuthenticatingFetchWithRetry(fetch, {
      headers: async () => ({ Authorization: `Bearer ${process.env.KEIKI_API_KEY}` }),
      shouldRetryWithHeaders: async () => undefined,
    })
    
    const client = await new ClientFactory({
      transports: [new JsonRpcTransportFactory({ fetchImpl: authFetch })],
      cardResolver: new DefaultAgentCardResolver({ fetchImpl: authFetch }),
    }).createFromUrl('https://onkeiki.com/a2a/<agent id>/.well-known/agent-card.json', '')
    
    const result = await client.sendMessage({
      tenant: '',
      message: {
        messageId: crypto.randomUUID(),
        contextId: 'order-1234',
        taskId: '',
        role: Role.ROLE_USER,
        parts: [{ content: { $case: 'text', value: 'Where is my order?' }, mediaType: 'text/plain', filename: '', metadata: undefined }],
        metadata: undefined,
        extensions: [],
        referenceTaskIds: [],
      },
      configuration: undefined,
      metadata: undefined,
    })

    The task completes with your agent's reply in two places: the final status message and a reply artifact. Send the next message with the same contextId and the conversation carries on.

The full reference is in the A2A docs.

Let other agents find yours

Keiki also publishes a card of its own at onkeiki.com/.well-known/agent-card.json. It works as a directory. Send it a message with your API key, ask "Which agents can I talk to?", and it answers with the Agent Card URL of every A2A-enabled agent that key can reach. A partner gets one key and one address, and finds the rest on their own.

How tasks map to conversations

A2A has its own vocabulary. Here's how each piece maps onto Keiki:

  • Every message starts a task. GetTask, ListTasks and CancelTask see only tasks created with the same API key. Finished tasks are kept for 30 days.
  • Cancelling stops the turn. CancelTask on a running task stops your agent's turn, not only the stream.
  • Contexts are private to a key. Two keys that send the same contextId get two separate conversations, so one caller can never read into another caller's thread.
  • One message per context at a time. If a caller sends a new message while your agent is still answering in that context, Keiki marks it rejected. The caller sends it again once the earlier task finishes.
  • Text, data and file links. Keiki reads text and data parts. Files sent by URL reach your agent as links. A message can be up to 100,000 characters, and a request body up to 1 MB.

What's not supported yet

Push notifications, SubscribeToTask and the gRPC and REST bindings aren't available yet, and inline file bytes aren't accepted. Every call goes over JSON-RPC, with streaming over SSE. We'll update the A2A docs as each one ships.

Who it's for

A2A is for teams whose agent has to work with someone else's agent:

  • Partners and marketplaces. List your agent where other agents look for help, and let them book, quote or check status without a custom API.
  • Multi-agent systems. An orchestrator built on another framework can hand off to your Keiki agent for the jobs it owns, such as support, scheduling or account questions.
  • Your own stack. Internal agents in LangGraph, CrewAI or the AI SDK can call the Keiki agent your customers already text, so both answer from one source of truth.

Already have an agent somewhere else and want it on Keiki's channels? That's the other direction: see Keiki on Vercel Connect and How to Turn Your API into a WhatsApp and iMessage Agent.

Turn it on

A2A is live for every Keiki agent today. Open your agent's Channels, turn on A2A and copy the Agent Card URL. New to Keiki? Talk to us and we'll help you set up your first agent.

FAQ

What is the A2A protocol? A2A, short for Agent2Agent, is an open protocol for AI agents to talk to each other across vendors and frameworks. An agent publishes an Agent Card describing its skills and endpoint. Other agents send it messages over JSON-RPC and get its reply back as a task.

What is an A2A Agent Card? An Agent Card is a JSON document at a well-known URL. It lists an agent's name, description, skills, endpoint and how to authenticate. On Keiki, each agent's card is at https://onkeiki.com/a2a/<agent id>/.well-known/agent-card.json while its A2A channel is on.

What's the difference between A2A and MCP? MCP connects an agent to tools and data. A2A connects an agent to other agents, which reason for themselves and answer as tasks. A Keiki agent can use both: MCP tools inside its turn, and A2A for agents that call it.

Which A2A versions does Keiki support? A2A v1.0, and v0.3 for older clients. Keiki picks the version from the A2A-Version header. If the header is missing, Keiki treats the client as v0.3.

How do callers authenticate? With one of your organization's Keiki developer API keys, sent as Authorization: Bearer sk_live_…. A key scoped to one agent can call only that agent, and revoking the key revokes its A2A access.

Does the agent remember earlier messages? Yes. Messages that share a contextId are one conversation. Contexts are private to the API key that started them.

How is A2A billed? Each A2A message is one ordinary turn of your agent, billed the same way as a conversation that arrives over SMS or any other channel.

Published · Announcements