Connecting external agents

Connect an external agent to your Runtype A2A Surface so it can discover and invoke your Capabilities through the Agent-to-Agent (A2A) protocol.

Before you begin

Before you connect an external agent, prepare the following:

  • An active A2A Surface in Runtype.
  • The Agent Card URL from the Endpoints tab.
  • An API key with the a2a_ prefix.
  • Access to the external agent platform.

Choose a connection method

Choose the method that matches the external agent platform:

Connect through an A2A-compatible platform

For a platform with native A2A support, follow these steps:

  1. Create an agent connection in the external platform.

  2. Select A2A or Agent-to-Agent as the connection type.

  3. Enter the Agent Card URL:

    https://api.runtype.com/v1/products/YOUR_PRODUCT_ID/surfaces/YOUR_SURFACE_ID/a2a/.well-known/agent-card.json

    Replace YOUR_PRODUCT_ID with your Product ID and YOUR_SURFACE_ID with your Surface ID.

  4. Configure the connection to use a Bearer token.

  5. Enter the API key from the Keys tab as the token.

  6. Save the connection and run its test.

The platform reads your skills from the Agent Card after it connects.

Build a custom integration

For a platform without native A2A support, fetch the Agent Card and send a JSON-RPC request with the following Python code:

1import requests
2
3agent_card_url = (
4 "https://api.runtype.com/v1/products/YOUR_PRODUCT_ID/"
5 "surfaces/YOUR_SURFACE_ID/a2a/.well-known/agent-card.json"
6)
7response = requests.get(agent_card_url)
8response.raise_for_status()
9agent_card = response.json()
10
11print(f"Agent: {agent_card['name']}")
12print(f"Skills: {len(agent_card.get('skills', []))}")
13
14a2a_url = (
15 "https://api.runtype.com/v1/products/YOUR_PRODUCT_ID/"
16 "surfaces/YOUR_SURFACE_ID/a2a"
17)
18payload = {
19 "jsonrpc": "2.0",
20 "method": "SendMessage",
21 "id": "request-1",
22 "params": {
23 "message": {
24 "role": "ROLE_USER",
25 "parts": [{"text": "What are your business hours?"}],
26 "messageId": "message-1"
27 },
28 "metadata": {"skill": "YOUR_SKILL_NAME"}
29 }
30}
31headers = {
32 "Authorization": "Bearer a2a_YOUR_API_KEY",
33 "Content-Type": "application/json"
34}
35result = requests.post(a2a_url, json=payload, headers=headers)
36result.raise_for_status()
37print(result.json())

Replace the placeholders in the sample as follows:

  • YOUR_PRODUCT_ID: your Product ID.
  • YOUR_SURFACE_ID: your Surface ID.
  • YOUR_SKILL_NAME: a skill name from the Agent Card.
  • a2a_YOUR_API_KEY: the key from the Keys tab.

In Managed mode, remove the metadata object so Runtype routes the request to a Capability.

Runtype also accepts the legacy v0.3 message/send method alias and typed message parts. Use the v1.0 method names and member-presence parts for new integrations.

Provide authentication

The Agent Card discovery endpoint is public, but the JSON-RPC invocation endpoint requires a valid A2A key.

To create a key, follow these steps:

  1. On your A2A Surface, open the Keys tab.
  2. Click Create Key.
  3. Enter a name for the key and click Create Key.
  4. Share the generated key with the external agent operator.

For each invocation, the external agent can send the key in an Authorization: Bearer header or an X-API-Key header. Create a separate API key for each external agent or organization to track usage and revoke access independently.

Test the connection

To verify that the external agent can invoke your Capabilities, follow these steps:

  1. Fetch the Agent Card from the external agent platform.
  2. Confirm that the Agent Card lists your skills.
  3. Invoke a skill with the SendMessage method.
  4. Open the execution logs in Runtype and confirm that the invocation appears.
  5. Confirm that the external agent receives the response.

Use a fallback agent

Use a fallback when a local agent cannot handle a request. This example treats responses with confidence less than 0.5 as fallback cases:

1async function handleCustomerQuery(query, localAgent, a2aUrl, a2aApiKey) {
2 const localResponse = await localAgent(query)
3
4 if (localResponse.confidence < 0.5) {
5 const response = await fetch(a2aUrl, {
6 method: 'POST',
7 headers: {
8 Authorization: `Bearer ${a2aApiKey}`,
9 'Content-Type': 'application/json',
10 },
11 body: JSON.stringify({
12 jsonrpc: '2.0',
13 method: 'SendMessage',
14 id: 'request-1',
15 params: {
16 message: {
17 role: 'ROLE_USER',
18 parts: [{ text: query }],
19 messageId: `message-${Date.now()}`,
20 },
21 },
22 }),
23 })
24
25 if (!response.ok) {
26 throw new Error(`A2A request failed with status ${response.status}`)
27 }
28
29 return response.json()
30 }
31
32 return localResponse
33}

Pass an asynchronous local agent function, the A2A endpoint URL, and the A2A API key to handleCustomerQuery.

Next steps