Guide

Webhooks

Push TeamShare events to your own servers. Create an endpoint, subscribe to events, and verify every delivery with an HMAC-SHA256 signature.

Overview

Webhooks are scoped to a company and managed by the company owner or an admin. Each endpoint subscribes to one or more events; when an event fires, the backend POSTs a signed JSON payload to the endpoint URL and records the delivery.

create an endpoint
curl -X POST https://api.teamshare.name.ng/webhooks \
  -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" \
  -d '{"companyId":"<company-id>","url":"https://example.com/hooks/teamshare","events":["task.created","task.resolved"]}'

The secret is returned once

The response includes the endpoint secret — the only time you see it. Store it with the endpoint; you need it to verify signatures.

Events

EventPayload highlights
task.createdtaskId, projectId, title, status, priority, actorId
task.updatedtaskId, projectId, title, status, actorId
task.resolvedtaskId, projectId, title, actorId
comment.createdcommentId, taskId, projectId, actorId
chat.message_createdmessageId, projectId, authorId, hasAttachment — never the body
env_var.createdenvVarId, projectId, key, tier, actorId — never the value
env_var.updatedenvVarId, projectId, key, tier, actorId
env_var.revealedenvVarId, projectId, key, tier, actorId
document.createddocumentId, projectId, type, actorId
invitation.acceptedinvitationId, projectId?, companyId?, role, actorId

Values never leave

Env-var payloads contain keys and tiers only — never values. Chat payloads carry the message id and an attachment flag, never the message body.

Verifying signatures

Every delivery sends two headers: X-TeamShare-Signature: sha256=<hex> and X-TeamShare-Timestamp: <unix-seconds>. The signature is an HMAC-SHA256 of `timestamp + "." + raw-body` using the endpoint secret — the timestamp prefix is replay protection. Always verify both before trusting a payload:

verify (Node.js)
const crypto = require("crypto");

function verifyTeamshareSignature(req, secret) {
  const timestamp = req.headers["x-teamshare-timestamp"];
  // Freshness first: reject replays older than 5 minutes.
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = `sha256=${crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${req.rawBody}`)
    .digest("hex")}`;
  const received = req.headers["x-teamshare-signature"];
  return received === expected;
}

Hash timestamp + raw body

Signatures are computed over `<unix-seconds>.<exact-bytes-received>`. Do not re-serialize the JSON — parse the body as raw bytes first, prefix the timestamp and dot, then hash.

Deliveries & test ping

Every delivery attempt is recorded — status (pending/delivered/failed), attempt count, last error, and timestamp. Owners and admins can list them and send a synthetic test.ping to verify an endpoint end to end.

deliveries + test ping
curl "https://api.teamshare.name.ng/webhooks/deliveries?endpointId=<endpoint-id>" \
  -H "Authorization: Bearer <jwt>"

curl -X POST https://api.teamshare.name.ng/webhooks/<endpoint-id>/test \
  -H "Authorization: Bearer <jwt>"

The test ping payload looks like: {"event":"test.ping","endpointId":"…","companyId":"…","triggeredAt":"…"} — signed with the same HMAC scheme, so it doubles as a signature integration test.