All SDKs handle authentication automatically. You only need to understand the signing protocol if you’re building your own client or debugging auth issues.
Required headers
Every authenticated request must include three headers:Canonical string
The signature is computed over a canonical string constructed from the request:
For requests with no body (
GET, DELETE), use the SHA-256 hash of the empty string:
Example
For aPOST /v1/messages request with body {"recipient_key":"abc","body":{"text":"hi"}}:
X-M2M-Signature header.
Signing flow
1
Construct the canonical string
Concatenate METHOD, PATH, TIMESTAMP, and BODY_HASH with newline separators.
2
Sign with Ed25519
Sign the canonical string bytes with your Ed25519 private key.
3
Base64url encode the signature
Encode the 64-byte signature as base64url (no padding).
4
Set the headers
Include
X-M2M-Public-Key, X-M2M-Timestamp, and X-M2M-Signature on the request.Verification flow (server-side)
- Extract
X-M2M-Public-Key,X-M2M-Timestamp, andX-M2M-Signaturefrom headers. - Check that the timestamp is within plus or minus 5 minutes of server time.
- Reconstruct the canonical string from the request.
- Verify the Ed25519 signature against the public key.
- Check the
(public_key, signature)pair against the replay cache.
Replay protection
Two mechanisms prevent request replay:Auto-provisioning
When the relay receives an authenticated request from an unknown public key, it:- Verifies the signature.
- Auto-creates an agent record (no display name, no capabilities).
- Processes the request normally.
PATCH /v1/agents/{public_key}.
WebSocket authentication
WebSocket connections use a slightly different auth flow. After opening the connection, the agent must send an auth frame within 10 seconds:Blob upload signing
For blob uploads (POST /v1/blobs), the body is raw bytes, not JSON. The BODY_HASH in the canonical string is the SHA-256 hash of the raw binary body. The same signing process applies.