Activity · Authentication
Sign an Activity request
Activity trusts requests signed by your host server. The signature binds the tenant, actor, method, path, query string, body bytes, and request time. Do not place the gateway secret in a browser or mobile client.
Required headers
Every /api/v1 request includes four gateway headers:
| Header | Value |
|---|---|
x-tenant-id | Tenant UUID selected by the host |
x-actor-id | Host-owned identity for the caller |
x-feedaas-timestamp | Current Unix time in seconds |
x-feedaas-signature | Lowercase hexadecimal HMAC-SHA256 signature |
The default timestamp tolerance is 300 seconds. Activity rejects stale timestamps before checking the signature.
Signature payload
Join these seven values with a newline character, including an empty query-string line when the request has no query:
timestamp
UPPERCASE_METHOD
request_path
raw_query_string
tenant_id
actor_id
sha256_hex(raw_body) Sign the resulting UTF-8 bytes with the deployment’s gateway secret, then encode the HMAC digest as lowercase hexadecimal.
The path starts with /, such as /api/v1/posts. The query string does not include ?. Hash the exact body bytes sent over HTTP. Reformatting JSON after signing changes the digest and invalidates the request.
Node.js signer
import { createHash, createHmac } from "node:crypto";
export function signActivityRequest({
secret,
timestamp,
method,
path,
query = "",
tenantId,
actorId,
body = ""
}) {
const bodyHash = createHash("sha256").update(body).digest("hex");
const payload = [
timestamp,
method.toUpperCase(),
path,
query,
tenantId,
actorId,
bodyHash
].join("\n");
return createHmac("sha256", secret).update(payload).digest("hex");
} Generate one timestamp and serialize the body once. Use those exact values for both signing and sending.
Failure behavior
| Condition | Status and code |
|---|---|
Missing x-tenant-id | 400 missing_tenant_id |
| Tenant ID is not a UUID | 400 invalid_tenant_id |
Missing x-actor-id | 400 missing_actor_id |
| Missing or invalid timestamp | 401 missing_timestamp or 401 invalid_timestamp |
| Timestamp outside the allowed window | 401 expired_timestamp |
| Missing signature | 401 missing_signature |
| Signature does not match | 401 invalid_signature |
Authentication errors use this shape:
{
"error": {
"code": "invalid_signature",
"message": "invalid x-feedaas-signature header"
}
} Security boundary
The current contract uses one gateway secret per Activity deployment, not one secret per tenant. Keep signing behind your own trusted gateway and rotate the deployment secret through operational configuration.
The signed actor_id says which actor the host is representing. It does not transfer business authorization to Activity.