> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# QWED A2A quick start for agent-to-agent verification

> Install QWED A2A, configure a persistent signing key, and run your first zero-trust agent-to-agent verification in about five minutes.

## Installation

```bash pip theme={null}
pip install qwed-a2a
```

```bash from source theme={null}
git clone https://github.com/QWED-AI/qwed-a2a.git
cd qwed-a2a
pip install -e ".[dev]"
```

> [!NOTE]
>
> QWED A2A requires Python 3.10+. The `cryptography` and `PyJWT` packages are installed automatically for JWT attestation support.

***

## Configure a signing key

QWED A2A signs every verdict with a persistent ECDSA P-256 key so attestations remain verifiable across restarts. Set `QWED_A2A_SIGNING_KEY_PEM` before importing `qwed_a2a` — without it the interceptor fails closed on the first call.

```bash theme={null}
openssl ecparam -name prime256v1 -genkey -noout \
  | openssl pkcs8 -topk8 -nocrypt \
  > qwed_a2a_signing_key.pem

export QWED_A2A_SIGNING_KEY_PEM="$(cat qwed_a2a_signing_key.pem)"
export QWED_A2A_DEPLOYMENT_ID="local-dev"
```

See [Crypto attestations](https://docs.qwedai.tech/a2a/crypto-attestations.md) for key rotation and multi-replica guidance.

***

## Your first verification

**1. Import the interceptor**

```python theme={null}
import asyncio
from qwed_a2a.interceptor import A2AVerificationInterceptor
from qwed_a2a.protocol.schema import AgentMessage, PayloadType
```

**2. Create an interceptor**

```python theme={null}
interceptor = A2AVerificationInterceptor()
```

This creates an interceptor with:

* All verification engines enabled
* Crypto attestation enabled (if packages available)
* `default_allow=True` trust boundary

**3. Build a test message**

```python theme={null}
message = AgentMessage(
    sender_agent_id="demo-sender",
    receiver_agent_id="demo-receiver",
    payload_type=PayloadType.FINANCIAL_TRANSACTION,
    payload={
        "data": {
            "claimed_total": 150.00,
            "line_items": [
                {"description": "Widget A", "amount": 50.00, "quantity": 2},
                {"description": "Widget B", "amount": 25.00, "quantity": 2},
            ]
        }
    }
)
```

**4. Run verification**

```python theme={null}
async def main():
    verdict = await interceptor.intercept(
        message,
        trace_id="quickstart_001"
    )

    print(f"Status:      {verdict.status.value}")
    print(f"Engine:      {verdict.engine_used}")
    print(f"Trace ID:    {verdict.audit_trace_id}")
    print(f"Attestation: {verdict.attestation_jwt[:50]}...")

asyncio.run(main())
```

**5. Check the output**

```text theme={null}
Status:      verified ✅
Engine:      finance_guard
Trace ID:    quickstart_001
Attestation: eyJhbGciOiJFUzI1NiIsInR5cCI6InF3ZWQtYTJhLWF0...
```

The financial totals match (50×2 + 25×2 = 150), so the verdict is **verified** with a signed attestation. `verified` is the only admissible status.

***

## Try different scenarios

> [accordion] Block a hallucinated total
>
> ```python theme={null}
> bad_message = AgentMessage(
>     sender_agent_id="demo-sender",
>     receiver_agent_id="demo-receiver",
>     payload_type=PayloadType.FINANCIAL_TRANSACTION,
>     payload={
>         "data": {
>             "claimed_total": 999.99,  # Wrong!
>             "line_items": [
>                 {"amount": 100.00, "quantity": 1},
>                 {"amount": 50.00, "quantity": 1},
>             ]
>         }
>     }
> )
>
> verdict = await interceptor.intercept(bad_message, trace_id="bad_001")
> print(verdict.status)  # blocked
> print(verdict.reason)  # "Mathematical hallucination detected..."
> ```

> [accordion] Block dangerous code
>
> ```python theme={null}
> code_message = AgentMessage(
>     sender_agent_id="code-agent",
>     receiver_agent_id="executor",
>     payload_type=PayloadType.CODE_EXECUTION,
>     payload={"code": "import os; os.system('rm -rf /')"}
> )
>
> verdict = await interceptor.intercept(code_message, trace_id="code_001")
> print(verdict.status)  # blocked
> print(verdict.reason)  # "Dangerous code patterns detected: os.system"
> ```

> [accordion] Catch logical contradictions
>
> ```python theme={null}
> logic_message = AgentMessage(
>     sender_agent_id="reasoning-agent",
>     receiver_agent_id="planner",
>     payload_type=PayloadType.LOGIC_ASSERTION,
>     payload={
>         "assertions": [
>             {"claim": "budget_approved", "negated": False},
>             {"claim": "budget_approved", "negated": True},
>         ]
>     }
> )
>
> verdict = await interceptor.intercept(logic_message, trace_id="logic_001")
> print(verdict.status)  # blocked
> print(verdict.reason)  # "Logical contradiction detected..."
> ```

***

## Run the FastAPI gateway

QWED A2A includes a ready-to-use HTTP gateway. Every call to `POST /a2a/intercept` must authenticate with a per-agent API key in the `X-API-Key` header. Configure keys through the `QWED_A2A_API_KEYS` environment variable, a JSON object mapping each key to its agent ID. There is no anonymous access: if no keys are configured, the endpoint denies every request with `401`.

```python theme={null}
from fastapi import FastAPI
from qwed_a2a.protocol.endpoints import router

app = FastAPI(title="QWED A2A Gateway")
app.include_router(router)

# Run with:
# QWED_A2A_API_KEYS='{"key-for-A": "agent-A"}' \
#   uvicorn main:app --host 0.0.0.0 --port 8000
```

Test it:

```bash theme={null}
curl -X POST http://localhost:8000/a2a/intercept \
  -H "Content-Type: application/json" \
  -H "X-API-Key: key-for-A" \
  -d '{
    "sender_agent_id": "agent-A",
    "receiver_agent_id": "agent-B",
    "payload_type": "general",
    "payload": {"message": "Hello!"}
  }'
```

> [!WARNING]
>
> The `X-API-Key` header selects the caller. The gateway processes and attests the message as the key's agent even if `sender_agent_id` in the body names someone else. Missing, unknown, or unconfigured keys return `401`. See [Gateway authentication](https://docs.qwedai.tech/a2a/deployment.md#gateway-authentication) for key format and rotation.

Response:

```json theme={null}
{
  "status": "unverifiable",
  "audit_trace_id": "a2a_7f3c2a1b9e4d",
  "engine_used": "passthrough",
  "attestation_jwt": null,
  "reason": "No verification engine available for this payload type"
}
```

`GENERAL` and `DATA_QUERY` payloads have no verification engine, so the interceptor returns an `unverifiable` verdict with **no JWT attestation**, since signing a token for content that was never verified would be a false cryptographic claim. Callers must decide how to handle unverified traffic. To see a signed `verified` verdict, send a well-formed `financial_transaction` payload instead; `logic_assertion` and `code_execution` payloads that pass return a signed `heuristic_pass`, which is not admissible. See the [verdict status table](https://docs.qwedai.tech/a2a/interceptor.md#verdict-statuses) for the full contract.

***

## Next steps

* [Architecture deep-dive](https://docs.qwedai.tech/a2a/architecture.md) — understand the full pipeline
* [Trust boundary](https://docs.qwedai.tech/a2a/trust-boundary.md) — configure zero-trust policies
* [Crypto attestations](https://docs.qwedai.tech/a2a/crypto-attestations.md) — understand JWT signing
* [Production deployment](https://docs.qwedai.tech/a2a/deployment.md) — Docker, monitoring, CI/CD


## Related topics

- [QWED A2A zero-trust protocol for agent-to-agent verification](https://docs.qwedai.tech/a2a/overview.md)
- [QWED A2A deployment for zero-trust agent gateways](https://docs.qwedai.tech/a2a/deployment.md)
- [QWED A2A architecture for agent verification](https://docs.qwedai.tech/a2a/architecture.md)
- [Changelog](https://docs.qwedai.tech/changelog.md)
- [QWED A2A verification interceptor pipeline and verdicts](https://docs.qwedai.tech/a2a/interceptor.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.