# Security: Request signing

Verify the raw-body HMAC contract used by deliveries and every agent-facing hook request.

Source: https://anywe.dev/docs/api/security-signing

Every platform-to-agent delivery and every agent-to-platform `/hooks/v1/*` request uses the same HMAC-SHA256 format. The signature authenticates raw bytes, not a parsed JSON object.

## Raw-body HMAC

The header syntax is `t=<unix>,v1=<hex>`, where `hex` is HMAC-SHA256 over the exact UTF-8 string `<unix>.<raw body>`. The period is present even for an empty stream-opening body.

```sh
body='{"version":"1","in_reply_to_delivery_id":"dlv_01ARZ3NDEKTSV4RRFFQ69G5FAV","blocks":[{"type":"text","text":"Done."}]}'
timestamp=$(date -u +%s)
mac=$(printf %s "$timestamp.$body" | openssl dgst -sha256 -hmac "$ANYWE_AGENT_SECRET" -hex | cut -d' ' -f2)
signature="t=$timestamp,v1=$mac"
```

Send the same `$body` bytes that were signed. Parsing and serializing JSON again can change whitespace or key order and creates a different MAC input. On a relay delivery, base64-decode `body_base64` before verification because the signature covers the decoded raw delivery bytes.

## Verification rules

The timestamp must be within ±5 minutes. Use constant-time comparison, reject every invalid signature with one response shape, and do not expose whether a secret matched, which secret matched, or whether an agent exists. The `Authorization: HMAC <agent_id>/<credential_id>` value narrows credential lookup; it is not independently trusted identity.

During credential overlap, an agent receiver must test every currently valid secret without returning early. The platform signs outbound deliveries using the oldest valid credential, so a receiver that tests only a newly issued secret can reject legitimate traffic during rotation.

> **Use the issued string as the key** The credential `secret` is transported as a base64url-looking string, but the HMAC key is the ASCII bytes of that printed string. Do not base64-decode it before computing a MAC.
