Search the complete documentation

Try idempotency, proposal lifecycle, authentication, or ordering.

Navigate Open
繁體中文
Browse documentation

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:

HeaderValue
x-tenant-idTenant UUID selected by the host
x-actor-idHost-owned identity for the caller
x-feedaas-timestampCurrent Unix time in seconds
x-feedaas-signatureLowercase 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

ConditionStatus and code
Missing x-tenant-id400 missing_tenant_id
Tenant ID is not a UUID400 invalid_tenant_id
Missing x-actor-id400 missing_actor_id
Missing or invalid timestamp401 missing_timestamp or 401 invalid_timestamp
Timestamp outside the allowed window401 expired_timestamp
Missing signature401 missing_signature
Signature does not match401 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.

Next: publish a post and read a timeline.