Developers

Webhooks.

Real-time platform events delivered to your endpoints. Subscribe to the events you care about, validate signatures, build whatever you need on top of Chat.

Quickstart.

Five minutes from reading this to receiving your first webhook event. Configure an endpoint URL in your Chat admin, copy the signing secret, paste the verification code below into your endpoint handler, trigger a test event from the admin. The first real event arrives the next time the relevant action happens on your account.

Node

import crypto from "node:crypto";

export function verifyChatWebhook(req, secret) {
  const signature = req.headers["x-chat-signature"];
  const timestamp = req.headers["x-chat-timestamp"];
  const eventId = req.headers["x-chat-event-id"];
  const rawBody = req.rawBody;

  if (!signature || !timestamp || !eventId) {
    throw new Error("Missing required webhook headers");
  }

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    throw new Error("Invalid signature");
  }
}

Python

import hashlib
import hmac
import time

def verify_chat_webhook(secret: str, raw_body: bytes, headers: dict) -> None:
    signature = headers.get("X-Chat-Signature")
    timestamp = headers.get("X-Chat-Timestamp")
    event_id = headers.get("X-Chat-Event-Id")
    if not signature or not timestamp or not event_id:
        raise ValueError("Missing required webhook headers")

    if abs(int(time.time()) - int(timestamp)) > 300:
        raise ValueError("Timestamp outside replay window")

    message = f"{timestamp}.".encode("utf-8") + raw_body
    expected = hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        raise ValueError("Invalid signature")

Go

func VerifyChatWebhook(secret string, rawBody []byte, headers http.Header) error {
    signature := headers.Get("X-Chat-Signature")
    timestamp := headers.Get("X-Chat-Timestamp")
    eventID := headers.Get("X-Chat-Event-Id")
    if signature == "" || timestamp == "" || eventID == "" {
        return errors.New("missing required webhook headers")
    }

    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(timestamp + "."))
    mac.Write(rawBody)
    expected := hex.EncodeToString(mac.Sum(nil))
    if !hmac.Equal([]byte(expected), []byte(signature)) {
        return errors.New("invalid signature")
    }
    return nil
}

Events reference

Workforce

hire.completed

Planned

Trigger A role hire finishes successfully on the account.

Idempotency key hire_id

Recommended use case Sync a completed hire to HR or internal provisioning.

Payload schema
  • hire_id string
  • role_slug string
  • started_at timestamp
Example payload
{
  "hire_id": "hire_123",
  "role_slug": "senior-engineer",
  "started_at": "2026-04-26T00:00:00Z"
}

Workforce

hire.fired

Planned

Trigger A role is terminated on the account.

Idempotency key hire_id

Recommended use case Update seat planning or HR records.

Payload schema
  • hire_id string
  • role_slug string
  • fired_at timestamp
Example payload
{
  "hire_id": "hire_123",
  "role_slug": "marketer",
  "fired_at": "2026-04-26T00:00:00Z"
}

Workforce

hire.fire_rescinded

Planned

Trigger A pending termination is reversed.

Idempotency key hire_id

Recommended use case Restore access or downstream staffing state.

Payload schema
  • hire_id string
  • role_slug string
  • rescinded_at timestamp
Example payload
{
  "hire_id": "hire_123",
  "role_slug": "marketer",
  "rescinded_at": "2026-04-26T00:00:00Z"
}

Workforce

hire.notice_completed

Planned

Trigger A role finishes a notice period.

Idempotency key hire_id

Recommended use case Trigger offboarding tasks.

Payload schema
  • hire_id string
  • role_slug string
  • notice_completed_at timestamp
Example payload
{
  "hire_id": "hire_123",
  "role_slug": "designer",
  "notice_completed_at": "2026-04-26T00:00:00Z"
}

Workforce

temp.completed

Planned

Trigger A temporary specialist finishes a scoped job.

Idempotency key temp_job_id

Recommended use case Move completed temp work into ops or billing flows.

Payload schema
  • temp_job_id string
  • role_slug string
  • completed_at timestamp
Example payload
{
  "temp_job_id": "temp_123",
  "role_slug": "designer",
  "completed_at": "2026-04-26T00:00:00Z"
}

Workforce

role.changed

Planned

Trigger A role assignment or configuration changes.

Idempotency key role_slug + changed_at

Recommended use case Mirror role changes to internal planning tools.

Payload schema
  • role_slug string
  • change_type string
  • changed_at timestamp
Example payload
{
  "role_slug": "senior-engineer",
  "change_type": "capacity_updated",
  "changed_at": "2026-04-26T00:00:00Z"
}

Pipeline

job.queued

Planned

Trigger A new job enters the execution queue.

Idempotency key job_id

Recommended use case Feed a project tracker or queue monitor.

Payload schema
  • job_id string
  • queue string
  • queued_at timestamp
Example payload
{
  "job_id": "job_123",
  "queue": "main",
  "queued_at": "2026-04-26T00:00:00Z"
}

Pipeline

job.started

Planned

Trigger A queued job begins execution.

Idempotency key job_id

Recommended use case Start SLA timers or status updates.

Payload schema
  • job_id string
  • role_slug string
  • started_at timestamp
Example payload
{
  "job_id": "job_123",
  "role_slug": "senior-engineer",
  "started_at": "2026-04-26T00:00:00Z"
}

Pipeline

job.completed

Planned

Trigger A job completes successfully.

Idempotency key job_id

Recommended use case Close tasks or notify downstream systems.

Payload schema
  • job_id string
  • role_slug string
  • completed_at timestamp
Example payload
{
  "job_id": "job_123",
  "role_slug": "senior-engineer",
  "completed_at": "2026-04-26T00:00:00Z"
}

Pipeline

job.failed

Planned

Trigger A job fails and requires attention.

Idempotency key job_id

Recommended use case Open incidents or retry orchestration.

Payload schema
  • job_id string
  • role_slug string
  • failed_at timestamp
Example payload
{
  "job_id": "job_123",
  "role_slug": "senior-engineer",
  "failed_at": "2026-04-26T00:00:00Z"
}

Pipeline

job.carried_over

Planned

Trigger A job is carried into a later cycle.

Idempotency key job_id

Recommended use case Keep planning tools in sync with slip events.

Payload schema
  • job_id string
  • carried_over_at timestamp
  • reason string
Example payload
{
  "job_id": "job_123",
  "carried_over_at": "2026-04-26T00:00:00Z",
  "reason": "capacity overflow"
}

Capacity

capacity.overflow_started

Planned

Trigger Capacity overflow begins for a role or queue.

Idempotency key role_slug + overflow_started_at

Recommended use case Alert operations when a team lane is saturated.

Payload schema
  • role_slug string
  • overflow_started_at timestamp
Example payload
{
  "role_slug": "senior-engineer",
  "overflow_started_at": "2026-04-26T00:00:00Z"
}

Capacity

capacity.overflow_resolved

Planned

Trigger Capacity overflow is cleared.

Idempotency key role_slug + resolved_at

Recommended use case Clear downstream alerts when throughput recovers.

Payload schema
  • role_slug string
  • resolved_at timestamp
Example payload
{
  "role_slug": "senior-engineer",
  "resolved_at": "2026-04-26T00:00:00Z"
}

Vibe Check

scan.completed

Planned

Trigger A scan completes on the account.

Idempotency key scan_id

Recommended use case Push scan results into a security dashboard.

Payload schema
  • scan_id string
  • repo_url string
  • completed_at timestamp
Example payload
{
  "scan_id": "scan_123",
  "repo_url": "https://github.com/acme/repo",
  "completed_at": "2026-04-26T00:00:00Z"
}

Vibe Check

scan.finding_critical

Planned

Trigger A critical finding is generated for a scan.

Idempotency key finding_id

Recommended use case Open urgent security workflows automatically.

Payload schema
  • scan_id string
  • finding_id string
  • severity string
Example payload
{
  "scan_id": "scan_123",
  "finding_id": "finding_123",
  "severity": "critical"
}

Vibe Check

scan.score_dropped

Planned

Trigger A repo score drops below a configured threshold.

Idempotency key scan_id + current_score

Recommended use case Escalate regressions to engineering or security.

Payload schema
  • scan_id string
  • previous_score integer
  • current_score integer
Example payload
{
  "scan_id": "scan_123",
  "previous_score": 92,
  "current_score": 71
}

Marketplace

marketplace.inquiry_received

Planned

Trigger A marketplace profile receives a new inquiry.

Idempotency key inquiry_id

Recommended use case Route inquiries into a CRM.

Payload schema
  • profile_id string
  • inquiry_id string
  • received_at timestamp
Example payload
{
  "profile_id": "profile_123",
  "inquiry_id": "inq_123",
  "received_at": "2026-04-26T00:00:00Z"
}

Marketplace

marketplace.profile_viewed

Planned

Trigger A marketplace profile view is recorded.

Idempotency key profile_id + viewed_at

Recommended use case Track interest from recruiting or customer teams.

Payload schema
  • profile_id string
  • viewed_at timestamp
Example payload
{
  "profile_id": "profile_123",
  "viewed_at": "2026-04-26T00:00:00Z"
}

Marketplace

marketplace.verification_status_changed

Planned

Trigger Marketplace verification status changes.

Idempotency key profile_id + changed_at

Recommended use case Mirror trust status into external systems.

Payload schema
  • profile_id string
  • status string
  • changed_at timestamp
Example payload
{
  "profile_id": "profile_123",
  "status": "verified",
  "changed_at": "2026-04-26T00:00:00Z"
}

Patron Funding

patron.contribution_received

Planned

Trigger A patron contribution lands and becomes Chat credit.

Idempotency key contribution_id

Recommended use case Thank patrons or update founder support dashboards.

Payload schema
  • contribution_id string
  • amount_usd number
  • received_at timestamp
Example payload
{
  "contribution_id": "contrib_123",
  "amount_usd": 50,
  "received_at": "2026-04-26T00:00:00Z"
}

Patron Funding

patron.first_contribution_today

Planned

Trigger The first patron contribution of the day lands.

Idempotency key founder_id + received_at

Recommended use case Trigger celebration or daily support workflows.

Payload schema
  • founder_id string
  • contribution_id string
  • received_at timestamp
Example payload
{
  "founder_id": "founder_123",
  "contribution_id": "contrib_123",
  "received_at": "2026-04-26T00:00:00Z"
}

Customer Council

council.vote_opened

Planned

Trigger A Customer Council vote opens.

Idempotency key vote_id

Recommended use case Notify policy or product councils.

Payload schema
  • vote_id string
  • opened_at timestamp
Example payload
{
  "vote_id": "vote_123",
  "opened_at": "2026-04-26T00:00:00Z"
}

Customer Council

council.vote_closed

Planned

Trigger A Customer Council vote closes.

Idempotency key vote_id

Recommended use case Close voting workflows and sync outcomes.

Payload schema
  • vote_id string
  • closed_at timestamp
Example payload
{
  "vote_id": "vote_123",
  "closed_at": "2026-04-26T00:00:00Z"
}

Signature verification

Verify the signature on every payload. Skipping signature verification means anyone who can guess your endpoint URL can send fake events. Do not skip it.

HeadersX-Chat-Event, X-Chat-Event-Id, X-Chat-Timestamp, X-Chat-Signature
AlgorithmHMAC-SHA256
Inputtimestamp + "." + raw_body
Replay window5 minutes unless explicitly replayed by admin

Retry policy

Chat treats any 2xx response as success. Everything else retries on an exponential schedule until the event either succeeds or moves into the dead letter queue.

  1. 30 seconds
  2. 2 minutes
  3. 10 minutes
  4. 30 minutes
  5. 2 hours
  6. 6 hours
  7. 12 hours
  8. 24 hours

After 8 failed attempts, the event moves to the dead letter queue for inspection and replay.

Health monitoring

Each endpoint tracks response rate, average latency, recent failures, dead-lettered events, and replay activity across 24h, 7d, and 30d windows.

Best practices

Troubleshooting