Based on: A worked playbook for a form, CRM or inbound-email webhook; the sender's signature scheme and field names are the parts to adapt.
The situation
Three teams, one inbox. A lead asks about plans (sales), a user says something is broken (support), a customer asks how to do something (success), and the first reply decides whether there is a second message. One skill can answer all three because the rules are the same: answer from the documentation and the pricing page as they are today, keep the record straight, answer every later message in the thread, and never decide money, dates or legal questions alone. The sender differs (a form tool, the CRM's events, an inbound-email webhook), and that is the part of the skill you adapt.
How it runs
- The sender. The form tool, the CRM or the inbound mailbox posts each new message to the skill's hosted URL. The file below verifies Standard Webhooks signatures, what Svix-based senders send; a sender with its own scheme gets an
hmacblock with its header, prefix, algorithm and encoding, and one that can only set a header getsbearerwith a secret skillhook generated. - The machine.
whenskips deliveries without a message text (a sender's other events);dedupeon the sender's message id means one run per message, however often it is retried, and skillhook folds an identical delivery into a run that is still going.concurrency: 2lets two threads run at once. - The job. It reads the message, the thread and the CRM record, stops when a person has taken over or the message was already answered, classifies the message, gathers the facts the answer needs, checks what a person must approve first, replies in the thread, and updates the CRM: the contact, the stage, a note with the reply and the facts used, and the message id under
agent_answered, the second layer of deduplication that outlives skillhook's 24-hour window. - The inbox. The job under "<name>: <subject>", the thread and the draft in the question's context, four buttons; the
needs_humanalert goes out.
Set it up with your agent
Paste this into Claude Code or Codex with the skillhook plugin. It installs skillhook, asks which sender you start with and adapts the signature scheme and the field paths, creates the skill, turns on the hosted URL, and walks you through a test lead and a test reply that must go to you first. Paste the SKILL.md from the next section as your second message.
The skill
The lines marked EDIT are the sender's: the signature scheme, where the text and the message id live. The body is the same for every sender: the stop conditions, the classification, the list of what a person approves first, the reply, the CRM update. Every frontmatter field is one skillhook validates.
Where a person comes in
- Before any exception. A price or terms not on the pricing page, a promise (a date, a feature, an SLA), anything legal (contracts, a DPA, a security questionnaire, a data request), a refund or a cancellation, a lead whose record has an owner, a lead who asks for a person: the skill drafts, then asks with Send the draft, Edit, I will take it and Stop. It waits up to thirty minutes with the job's timeout clock paused; without an answer the run ends with
needs_humanand the pick in the inbox resumes it. - It stops when a person says so. "Stop" in the inbox sets
agent: pausedon the CRM record and the skill never answers that thread again; a colleague's reply in the thread (an address in the company's domains) stops it too, as does "I will take it", which sets the owner. - Alerts.
needs_humanfor every open question;job_failedwhen the CRM or the mail API refuses the key.
What to check after
- Deliveries. A
duplicateis the sender retrying a message already answered; askippedone is an event without a message text; a rejected one names the reason,invalid_signatureormissing_tokenwhen the secret or the scheme does not match the sender's. - The job. The reply and the facts used in its summary, the thread as
source, the reply asmessage, the CRM record asdocument; the note on the record is the audit trail on the CRM's side. - Replay once a cause is fixed (a key, the field paths after the sender changed its payload):
replay_deliveryruns the message again, and the skill finds its id underagent_answeredand never answers twice.