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

# Signature test vectors

> Frozen known-answer examples for REST, inbound, and outbound HMAC signing.

Use these vectors to verify your signer matches EchoZero and the official SDK.

<Warning>
  **Inbound vs outbound:** Inbound agent signals sign **canonical JSON** (sorted keys, known field set). Outbound `signal.execution` webhooks sign the **raw request body** string. REST HMAC signs `timestamp + METHOD + path + body`.
</Warning>

## REST HMAC (POST)

| Field                  | Value                                                              |
| ---------------------- | ------------------------------------------------------------------ |
| Secret                 | `test_secret`                                                      |
| Timestamp (ms)         | `1710000000000`                                                    |
| Method                 | `POST`                                                             |
| Path                   | `/api/api-keys`                                                    |
| Body                   | `{"name":"SDK HMAC Test"}`                                         |
| String to sign         | `1710000000000POST/api/api-keys{"name":"SDK HMAC Test"}`           |
| Expected `x-signature` | `274c9ff280eadf751530e9e7fce2c2a573d8676b13b7108fe353407df7cc9e00` |

## Inbound webhook (text + idempotencyKey)

| Field                     | Value                                                                  |
| ------------------------- | ---------------------------------------------------------------------- |
| Secret                    | `test_secret`                                                          |
| Timestamp (s)             | `1710000000`                                                           |
| Body                      | `{"idempotencyKey":"sdk-test-1","text":"BUY SOL 500 USDC"}`            |
| Canonical JSON            | `{"idempotencyKey":"sdk-test-1","text":"BUY SOL 500 USDC"}`            |
| Payload                   | `1710000000.{"idempotencyKey":"sdk-test-1","text":"BUY SOL 500 USDC"}` |
| Expected `X-EZ-Signature` | `1d30d896fc609e62bcf9be991c1dc1177a9c63219909869ef2846bad4685de3b`     |

## Inbound webhook (structured buy, unknown fields stripped)

| Field                     | Value                                                                |
| ------------------------- | -------------------------------------------------------------------- |
| Secret                    | `secret`                                                             |
| Timestamp (s)             | `1710000000`                                                         |
| Body fields               | `eventType`, `idempotencyKey`, `reasoning`, `tokenAddress`, `amount` |
| Extra fields              | Ignored for signing (e.g. `unknownField`)                            |
| Expected `X-EZ-Signature` | `be420f61d91e6b871481774c62f972f0aafa6f5f1a727ba1e4a32558784f77c3`   |

Canonical JSON for that vector:

```json theme={null}
{"amount":500,"eventType":"buy","idempotencyKey":"test-1","reasoning":"test","tokenAddress":"So11111111111111111111111111111111111111112"}
```

## Inbound canonicalization rules

1. Only signing-eligible keys are included (`text`, `idempotencyKey`, and structured fields from the `#1098` schema).
2. Object keys are sorted alphabetically at every nesting level.
3. Unknown top-level fields are **omitted** from the canonical form.
4. Numbers must be finite JSON numbers (no `NaN` / `Infinity`).

Match the SDK implementation: [`inbound_canonical.rs`](https://github.com/EchoZeroApp/echozero-sdk/blob/main/packages/rust/src/inbound_canonical.rs) (reference for all languages).

## Outbound `signal.execution`

| Field                           | Value                                                                                                                                     |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Secret                          | `webhook_secret`                                                                                                                          |
| Timestamp                       | `2026-07-06T12:00:00.000Z`                                                                                                                |
| Raw body                        | `{"event":"signal.execution","signalId":"sig_1","developerAgentId":"agent_1","status":"executed","timestamp":"2026-07-06T12:00:00.000Z"}` |
| Payload                         | `{timestamp}.{raw_body}`                                                                                                                  |
| Algorithm                       | HMAC-SHA256 → lowercase hex                                                                                                               |
| Expected `x-echozero-signature` | `249d84b9e89206f6aee4b124f728c945b805666b3102d76b913a17f1bf568574`                                                                        |

Verify with the same raw bytes you receive on the wire (do not re-serialize JSON).

## Inbound webhook (nested buy with triggers + context)

| Field                     | Value                                                              |
| ------------------------- | ------------------------------------------------------------------ |
| Secret                    | `test_secret`                                                      |
| Timestamp (s)             | `1710000000`                                                       |
| Canonical JSON            | see below                                                          |
| Expected `X-EZ-Signature` | `3afa3bf158361edcb46dbeaee6b128f7cd1c78baa28824f2355de1223671e67a` |

```json theme={null}
{"amount":500,"chain":"solana","context":{"indicators":["VWAP","RSI"],"marketCondition":"trending","price":148.5},"eventType":"buy","idempotencyKey":"nested-vector-001","instrument":{"tokenAddress":"So11111111111111111111111111111111111111112"},"reasoning":"Nested canonicalization test.","symbol":"SOL","triggers":{"slPrice":{"targetType":"price","value":165},"tps":[{"label":"TP1","sizePct":50,"targetType":"price","value":180}]}}
```

Nested objects must be sorted at every level (`slPrice` before `tps`; `targetType` before `value` inside each TP). This is where cross-language signing bugs usually appear.

## curl self-test (REST)

```bash theme={null}
SECRET="test_secret"
TS="1710000000000"
REQ_PATH="/api/api-keys"
BODY='{"name":"SDK HMAC Test"}'
SIG=$(printf '%s' "${TS}POST${REQ_PATH}${BODY}" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
echo "$SIG"
# expect 274c9ff280eadf751530e9e7fce2c2a573d8676b13b7108fe353407df7cc9e00
```
