> Source: https://wexa.ai/docs/agents/register-peer-agent

# register-peer-agent

[Can be held for approval](/docs/sdks-and-apis/how-every-tool-call-works#lifecycle-and-governance)

Register an external agent on another platform as a peer agent connection, so that a process flow can hand a whole goal to it. It returns the `connection_id` that an external agent node takes as `registration_id`. Needs the `fabric:orchestrate.write` grant.

The shape of `result` for `register-peer-agent` is not recorded here: it was not captured against a live deployment. Call it once and read what comes back rather than assuming a shape.

**Register a peer agent**

**Wexa MCP server**

```bash
curl -sS -X POST https://fabric.wexa.ai/mcp/$FABRIC_PROJECT_ID \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "Mcp-Session-Id: $MCP_SESSION_ID" \
  -H "content-type: application/json" -H "accept: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"register-peer-agent","arguments":{"name":"invoice-reconciler","endpoint":"https://agents.example.com/a2a/invoice-reconciler","protocol":"a2a","secret":"<token>"}}}'
```

**REST API**

```bash
curl -sS https://fabric.wexa.ai/v1/register-peer-agent \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "content-type: application/json" \
  -d '{"name":"invoice-reconciler","endpoint":"https://agents.example.com/a2a/invoice-reconciler","protocol":"a2a","secret":"<token>"}'
```

**TypeScript SDK**

```ts
import { Fabric } from '@wexa-fabric/sdk'

const fabric = new Fabric()
const result = await fabric.registerPeerAgent({
  name: 'invoice-reconciler',
  endpoint: 'https://agents.example.com/a2a/invoice-reconciler',
  protocol: 'a2a',
  secret: '<token>'
})
```

**Python SDK**

```python
from wexa import Fabric

fabric = Fabric()
out = fabric.register_peer_agent(
    name="invoice-reconciler",
    endpoint="https://agents.example.com/a2a/invoice-reconciler",
    protocol="a2a",
    secret="<token>",
)
```

## Parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `name` | string | Required | Non-empty. Unique within this project; a duplicate is refused (502), the message says so, and nothing is created: pick a different name. |
| `endpoint` | string | Required | The external agent's full `http://` or `https://` URL. Non-empty. |
| `protocol` | string | Required | `mcp` or `a2a`: which protocol the external agent speaks. Any other value is refused, and the refusal names both. |
| `secret` | string | Optional | The credential. Sent as `Authorization: Bearer <secret>` on every call; no header when empty. Supplied once, here; no tool ever returns it. |
| `description` | string | Optional | A description of the connection. |
| `platform` | string | Optional | A label for which platform this agent runs on. |
| `auth` | string | Optional | Only `key` works today (the default). `oauth` and `mtls` are refused: they are not supported yet. |

**More details**

Test the endpoint first with [handshake-peer-agent](/docs/agents/handshake-peer-agent). Registration runs its own handshake as well and records what the external agent says about itself; [list-peer-agents](/docs/agents/list-peer-agents) shows it afterwards. The `name` is the only thing telling two connections apart anywhere a person looks, which is why it must be unique. A connection holds your credential once, and no tool returns it afterwards, in any form.

**How the secret is sent.** When `secret` is set, it goes as `Authorization: Bearer <secret>` on every call to the external agent. When it is empty, no header is sent. Wexa does not fetch or refresh tokens, so a short-lived token, such as a Google access token, stops working when it expires.

**An MCP external agent must expose exactly one tool.** Registration runs its own handshake, and when an MCP external agent exposes more tools than one, or none, that handshake fails. The connection is still recorded, but an external agent node that points at it fails when it runs. Run `handshake-peer-agent` first, and register only an endpoint whose handshake succeeds.

This registers the console's "single peer agent" connection type, which takes a whole goal. The console's other connection type, a tool catalog, is a different concept and is console-only.

### Use it from a process flow

Put the `connection_id` on an `interop_agent` node, the external agent node, of a process flow as `registration_id`. The node carries no skills and no approval step: the external agent brings its own abilities and runs its step on its own platform. The full example, with the node's manifest, is in [an external agent node, end to end](/docs/automation/create-process-flow#an-external-agent-node-end-to-end).

When the process flow runs, the node hands the external agent its node prompt, the run goal and the context sources that are on for the node. A `{{goal}}` in the node prompt is filled with the `goal` you give `run-process-flow`. Set `context_access` to `{"mode": "off"}` for an external agent you do not trust, so that project information does not go to a third party. Poll the run with [get-execution](/docs/agents/get-execution), because a node like this can stay open for a long time and can stop to ask a question.

An external agent returns no token usage, so an external step shows only what Wexa spends on its own side. A question from an external agent over `mcp` is cancelled after 3 minutes with no answer. A question over `a2a` has no time limit, and no tool cancels a parked run.

### Rotate a credential

There is no update operation, so change a secret by replacing the connection.

1. Register a new connection with the new secret.
2. Point every `interop_agent` node at the new connection id with `update-process-flow` (`registration_id`).
3. Delete the old connection with [delete-peer-agent](/docs/agents/delete-peer-agent).

Do step 2 before step 3: `delete-peer-agent` refuses while a node still uses the connection.

### Result fields

- `connection_id`: the id an `interop_agent` node takes as `registration_id`.
- `name`, `protocol`, `endpoint`: what you registered.
- `state`: `Draft` when registered, `Active` once a call to the external agent has gone through.

The secret is not in the result.

Every tool call works the same way: [How every tool call works](/docs/sdks-and-apis/how-every-tool-call-works).
