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.
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
| Event | Payload highlights |
|---|---|
| task.created | taskId, projectId, title, status, priority, actorId |
| task.updated | taskId, projectId, title, status, actorId |
| task.resolved | taskId, projectId, title, actorId |
| comment.created | commentId, taskId, projectId, actorId |
| chat.message_created | messageId, projectId, authorId, hasAttachment — never the body |
| env_var.created | envVarId, projectId, key, tier, actorId — never the value |
| env_var.updated | envVarId, projectId, key, tier, actorId |
| env_var.revealed | envVarId, projectId, key, tier, actorId |
| document.created | documentId, projectId, type, actorId |
| invitation.accepted | invitationId, 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:
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.
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.