Agent skill

Connecteam Webhooks Skill

Receive and authenticate Connecteam webhooks. Use when setting up a Connecteam webhook endpoint, checking the x-webhook-secret header, debugging 401s, or handling workforce events like user_created, form_submission, clock_in, clock_out, shift_created and task_completed.

Install this skill

npx skills add hookdeck/webhook-skills --skill connecteam-webhooks


Connecteam, the frontline workforce app, sends a JSON webhook when something happens in users, forms, the time clock (time activity), the shift scheduler, tasks or chat. You create webhooks through the Connecteam Webhook API. Webhooks are not signed: when you set a secretKey, Connecteam sends that exact value in the x-webhook-secret header and does not sign or hash the body. Your endpoint authenticates the request by comparing that header with the secret you stored.

Set a secretKey when you create the webhook through the Connecteam API. Webhooks without one send nothing to verify.

When to Use This Skill

  • How do I receive Connecteam webhooks?
  • How do I verify the x-webhook-secret header?
  • Does Connecteam sign webhook payloads? (No: it sends the shared secretKey as-is)
  • How do I handle user_created, form_submission, clock_in or shift_created events?
  • Why does my Connecteam webhook endpoint return 401?

Verification (core)

const crypto = require('crypto');

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyConnecteamRequest(headers, secret = process.env.CONNECTEAM_WEBHOOK_SECRET) {
  if (!secret) return false;                       // never accept when unconfigured
  const received = headers['x-webhook-secret'];    // the secretKey, sent unchanged
  return Boolean(received) && safeEqual(received, secret); // compare the WHOLE value
}
  • Header: x-webhook-secret: <secretKey> (case-insensitive name, no prefix, not a hash).
  • Return 401 when the header is missing or doesn't match, 200 (any 2xx) once handled.
  • Python: hmac.compare_digest on bytes (encode both sides; passing str raises on non-ASCII).
  • Read the body raw (express.raw({ type: '*/*' })) and authenticate before parsing it.

What this does and doesn't give you. A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Anyone who captures one request can replay it or send a different body with the same header. Use HTTPS, and rotate the secretKey if it leaks.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

HTTP Methods

Only the Chat webhook guide names the method (POST, with User-Agent: connecteam); the setup page doesn't. The examples accept GET, POST, PUT, PATCH and DELETE on the route, check the credential first for every method, and return 200 for an empty body.

Hookdeck's Connecteam source pre-selects POST without managing the method, so any other method the sender uses must also be allowed on the source.

Event Types

Dispatch on eventType. Most features send {requestId, company, activityType, eventTimestamp, eventType, data}; Time Activity is flat (userId, timeClockId, timeActivity at the root, plus webhookVersion).

FeatureEvents
Usersuser_created, user_updated, user_deleted, user_archived, user_restored, user_promoted, user_demoted
Formsform_submission, form_submission_edited, manager_field_updated
Time Activityclock_in, clock_out, auto_clock_out, admin_add, admin_edit, admin_delete, user_timesheet_submitted, user_timesheet_unsubmitted, user_timesheet_approved, user_timesheet_reopened, plus request approval and decline events
Shift Schedulershift_created, shift_updated, shift_deleted, availability_status_created, availability_status_deleted
Taskstask_published, task_completed
Chatmessage_created, message_updated, message_deleted, conversation_created, conversation_updated, conversation_deleted

Return any 2xx within 10 seconds, then process asynchronously. A 404 is treated as permanent.

Environment Variables

CONNECTEAM_WEBHOOK_SECRET=your-webhook-secret-key   # the secretKey you set on the webhook

Local Development

Connecteam can't reach localhost. Tunnel with the Hookdeck CLI (no account required):

npx hookdeck-cli listen 3000 connecteam --path /webhooks/connecteam

Use the printed URL as the webhook url when you create the webhook through the API.

Resources


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 9, 2026

View on GitHub →