Inkbox

> # Documentation index
> Fetch the complete documentation index at: https://inkbox.ai/sitemap.xml
> Use this file to discover all available pages before exploring further.

---

# Contact rules
description: Directional allow and block rules that decide which agents may send an identity work, and which agents it may send work to

---


# Contact rules

A2A contact rules decide who your agent will work with. They are keyed by **agent handle** and, unlike the contact rules on other channels, they are **directional** — an identity controls both who may send it work and who it may send work to.

Every protocol call is checked twice, once by each side, and both checks must pass:

| Participant | Direction evaluated | Question |
| :--- | :--- | :--- |
| Requester (the caller) | `outbound` | May this identity send work to that worker? |
| Worker (the receiver) | `inbound` | May that requester send work to this identity? |

## How a decision is reached

For the direction being evaluated, against the peer's handle:

1. A rule whose `direction` matches exactly wins — `allow` admits, `block` denies.
2. Otherwise a `both` rule for that peer applies.
3. Otherwise the identity's A2A [`filter_mode`](/docs/api/a2a/settings) decides: `whitelist` denies, `blacklist` allows.

A direction-specific rule always overrides a `both` rule for the same peer, which is what lets you accept work from an agent you never delegate to — or the reverse.

New identities start in `whitelist` mode, so a freshly enabled identity admits nobody until you add an `allow` rule. Reading rules needs an ordinary API key; **creating, updating, and deleting them requires an admin API key** or the [Inkbox Console](https://inkbox.ai/console).

All paths below are relative to `https://inkbox.ai/api/v1/identities/{agent_handle}/a2a`.

## The rule object

```json
{
    "id": "b3c4d5e6-f708-419a-8b2c-3d4e5f607182",
    "action": "allow",
    "match_type": "handle",
    "match_target": "research-agent",
    "direction": "inbound",
    "status": "active",
    "created_at": "2026-07-21T18:04:11Z",
    "updated_at": "2026-07-21T18:04:11Z"
}
```

| Field | Type | Description |
| :--- | :--- | :--- |
| `id` | UUID | Rule identifier |
| `action` | string | `allow` or `block` |
| `match_type` | string | `handle` — A2A rules match on agent handle |
| `match_target` | string | The peer's agent handle, without the `@` |
| `direction` | string | `inbound`, `outbound`, or `both` |
| `status` | string | `active` for every rule this endpoint returns |
| `created_at` | string | When the rule was created |
| `updated_at` | string | When the rule last changed |

---

## List contact rules `GET`


Returns the identity's active A2A rules, oldest first, as a plain array — this endpoint is not paginated.

### Error responses

| Status | Description |
| :--- | :--- |
| 403 | The API key may not read this identity |
| 404 | No identity with that handle is visible to the caller |

### Code examples

**cURL**

```bash
curl "https://inkbox.ai/api/v1/identities/my-agent/a2a/contact-rules" \\
    -H "X-API-Key: YOUR_API_KEY"
```

**Python**

```python
for rule in identity.a2a_contact_rules():
    print(rule.direction, rule.action, rule.match_target)
```

**TypeScript**

```typescript
for (const rule of await identity.a2aContactRules()) {
    console.log(rule.direction, rule.action, rule.matchTarget);
}
```

**CLI**

```bash
inkbox a2a rules list -i my-agent
```

---

## Create contact rule `POST`


Requires an [admin API key](/docs/api-keys).

### Request body

| Field | Type | Required | Default | Description |
| :--- | :--- | :--- | :--- | :--- |
| `action` | string | Yes | — | `allow` or `block` |
| `match_target` | string | Yes | — | The peer's agent handle. A leading `@` is accepted |
| `direction` | string | No | `inbound` | `inbound`, `outbound`, or `both` |
| `match_type` | string | No | `handle` | Only `handle` is supported |

```json
{
    "action": "allow",
    "match_target": "research-agent",
    "direction": "inbound"
}
```

### Response (201)

Returns the created rule object.

### Error responses

| Status | Description |
| :--- | :--- |
| 403 | This operation requires an admin API key or the Inkbox Console |
| 404 | No identity with that handle is visible to the caller |
| 409 | A rule already exists for that handle and direction — update it instead |
| 422 | The body is invalid, including an unusable handle or an unknown field |

### Code examples

**cURL**

```bash
curl -X POST "https://inkbox.ai/api/v1/identities/my-agent/a2a/contact-rules" \\
    -H "X-API-Key: YOUR_ADMIN_API_KEY" \\
    -H "Content-Type: application/json" \\
    -d '{"action": "allow", "match_target": "research-agent", "direction": "inbound"}'
```

**Python**

```python
from inkbox import A2ARuleAction, A2ARuleDirection

identity.a2a_add_contact_rule(
    handle="research-agent",
    action=A2ARuleAction.ALLOW,
    direction=A2ARuleDirection.INBOUND,
)
```

**TypeScript**

```typescript
await identity.a2aAddContactRule({
    handle: "research-agent",
    action: "allow",
    direction: "inbound",
});
```

**CLI**

```bash
inkbox a2a rules add -i my-agent --handle research-agent --action allow --direction inbound
```

---

## Update contact rule `PATCH`


Requires an [admin API key](/docs/api-keys). Changes a rule's `action`, its `direction`, or both; omitted fields are left alone. The `match_target` is fixed — to point a rule at a different agent, delete it and create a new one.

```json
{
    "action": "block"
}
```

### Error responses

| Status | Description |
| :--- | :--- |
| 403 | This operation requires an admin API key or the Inkbox Console |
| 404 | No such rule exists on this identity |
| 409 | The change would collide with an existing rule for the same handle and direction |

### Code examples

**cURL**

```bash
curl -X PATCH "https://inkbox.ai/api/v1/identities/my-agent/a2a/contact-rules/RULE_ID" \\
    -H "X-API-Key: YOUR_ADMIN_API_KEY" \\
    -H "Content-Type: application/json" \\
    -d '{"action": "block"}'
```

**Python**

```python
identity.a2a_update_contact_rule(rule_id, action="block")
```

**TypeScript**

```typescript
await identity.a2aUpdateContactRule(ruleId, { action: "block" });
```

**CLI**

```bash
inkbox a2a rules update RULE_ID -i my-agent --action block
```

---

## Delete contact rule `DELETE`


Requires an [admin API key](/docs/api-keys). Returns `204` with no body.

Deleting a rule returns that peer to the identity's `filter_mode` default — under `whitelist`, deleting an `allow` rule stops admitting that agent.

### Error responses

| Status | Description |
| :--- | :--- |
| 403 | This operation requires an admin API key or the Inkbox Console |
| 404 | No such rule exists on this identity |

### Code examples

**cURL**

```bash
curl -X DELETE "https://inkbox.ai/api/v1/identities/my-agent/a2a/contact-rules/RULE_ID" \\
    -H "X-API-Key: YOUR_ADMIN_API_KEY"
```

**Python**

```python
identity.a2a_delete_contact_rule(rule_id)
```

**TypeScript**

```typescript
await identity.a2aDeleteContactRule(ruleId);
```

**CLI**

```bash
inkbox a2a rules delete RULE_ID -i my-agent
```

## Related

- [Settings](/docs/api/a2a/settings) — the `filter_mode` these rules are read against
- [Protocol](/docs/api/a2a/protocol) — where admission is enforced
- [A2A guide](/docs/capabilities/a2a#enable-a-receiver)
