Agent skill

TutorCruncher Webhooks Skill

Receive and verify TutorCruncher webhooks (tutoring agency management software). Use when setting up a TutorCruncher integration endpoint, debugging Webhook-Signature verification, or handling actions such as CREATED_A_CONTRACTOR, CREATED_AN_APPOINTMENT, CLIENT_ENQUIRY and RAISED_INVOICE.

Install this skill

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


TutorCruncher sends a webhook for every action on the account (a tutor created, a client enquiry, an invoice raised, ...). Each request body is {"events": [...]} and each element carries its own action.

When to Use This Skill

  • How do I receive TutorCruncher webhooks?
  • How do I verify the TutorCruncher Webhook-Signature header?
  • Why does my TutorCruncher signature check fail, or return 401?
  • How do I handle CREATED_A_CONTRACTOR, CREATED_AN_APPOINTMENT or CLIENT_ENQUIRY actions?

Verification (core)

Webhook-Signature is the bare lowercase hex HMAC-SHA256 of the raw request body, keyed with your integration's private API key (there is no separate webhook secret). No prefix, no id and no timestamp. The header name matches Standard Webhooks, but this is not Standard Webhooks. Compare the whole header value as one digest.

const crypto = require('crypto');

function verifyTutorCruncherSignature(rawBody, signature, secret) {
  // rawBody: Buffer of the exact bytes received (empty Buffer for no body)
  if (!secret || typeof signature !== 'string' || !/^[0-9a-fA-F]{64}$/.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const received = Buffer.from(signature.toLowerCase(), 'utf8');    // hex is case-insensitive
  const wanted = Buffer.from(expected, 'utf8');
  return received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
}
  • The key is the secret's UTF-8 bytes as-is (not hex- or base64-decoded).
  • Missing or empty header, malformed value or mismatch: 401.
  • Nothing time-based is signed, so there is no replay window to check.
  • Read the body raw (express.raw({ type: '*/*' })) and parse JSON only after the check.
  • Python: hmac.compare_digest on bytes, never str.

HTTP method: TutorCruncher's docs don't name the method, so accept GET, POST, PUT, PATCH and DELETE, verify the signature first for each, and treat a missing body as zero bytes (a verified empty body gets 200). Hookdeck's TutorCruncher source pre-selects POST without managing the method, so allow any other method the sender uses on the source too.

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

Hookdeck Setup

Hookdeck's TutorCruncher source type verifies the signature (HMAC, SHA-256, header webhook-signature, hex). Hookdeck shows this note for the source:

"Paste your TutorCruncher private API key. TutorCruncher signs webhooks with it."

Common Event Types

The event name is the action of each element in events. Loop over every element.

actionFired when
CREATED_A_CONTRACTORA Tutor is created
CHANGED_CONTRACTOR_STATUSA Tutor's status is updated
CREATED_A_CLIENTA Client is created
CLIENT_ENQUIRYA Client makes an Enquiry
CREATED_A_SERVICEA Job is created
CREATED_AN_APPOINTMENTA Lesson is created
MARKED_AN_APPOINTMENT_AS_COMPLETEA Lesson is marked as complete
RAISED_INVOICEAn Invoice is sent (once per Invoice)

For the full list, see the Action Types section of the TutorCruncher API reference.

Environment Variables

TUTORCRUNCHER_WEBHOOK_SECRET=your_private_api_key   # the integration's private API key

Delivery and Retries

Timeouts, no response and 5xx are retried up to 4 times with exponential backoff (after 1 s, 10 s, 100 s and 1000 s). Redirects and 4xx other than 429 are not retried. An endpoint whose failure rate stays above 20% is disabled. Each integration keeps webhook logs with request and response data.

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 tutorcruncher --path /webhooks/tutorcruncher

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 13, 2026

View on GitHub →