How to Onboard New Hires from Emailed HR Forms with a Mailhook

Forward a new-hire form email to a Spojit mailhook, extract the candidate's details from the attached PDF, and create a Deputy employee once a manager approves.

What This Integration Does

Many HR teams still receive new-hire paperwork as a PDF emailed by a recruiter, a hiring manager, or a candidate. Re-keying names, contact details, and start dates into Deputy by hand is slow and error-prone. This Spojit workflow turns that inbox step into an automation: HR forwards (or BCCs) the new-hire form to a dedicated mailhook address, Spojit reads the PDF, pulls the relevant fields, pauses for a manager to confirm, and then creates the employee in Deputy. A Slack message keeps the HR channel informed at the end.

The workflow is triggered by a Mailhook trigger, so each email that lands at the generated address starts one run within seconds. The PDF bytes are fetched with an Attachment node, a Connector node in Agent mode extracts the fields into structured JSON, and a Human node holds the run until a manager approves. Nothing is written to Deputy before approval, so a rejected or timed-out request leaves no record behind. Runs are deduplicated per message, and each forwarded email produces its own independent execution you can inspect in the run history.

Prerequisites

  • A Deputy connection with permission to create employees (the create-employee tool writes to the Employee resource).
  • A Slack connection authorized to post to your HR channel, and the channel ID or name you want to notify.
  • The pdf utility connector (built in, no authentication) for reading text from the attached form.
  • At least one approver set up: a User, Role, or Team you can assign to the Human node's approval slot. See Using Human Approval Nodes.
  • A consistent new-hire form layout. The cleaner and more predictable the PDF, the more reliable the extraction.

Step 1: Add a Mailhook trigger and generate the address

Create a new workflow and set the Trigger node type to Mailhook. Optionally set an Address prefix (1-24 characters, default mh) such as new-hire, then click Generate email address. Spojit produces a unique address in the form new-hire-<random16>@mailhook.spojit.com. Copy it and give it to HR as the forwarding target, or add it as a BCC on the recruiter's submission template.

To keep stray mail out, set an optional From allowlist (for example your HR and recruiting domains) and a Subject regex like (?i)new hire. The mailhook fires whether the address is in To, Cc, or Bcc. Each run exposes the email as {{ input }}, including {{ input.subject }}, {{ input.from }}, {{ input.replyTo }}, and {{ input.attachments }}.

Step 2: Fetch the PDF with an Attachment node

Add an Attachment node directly after the trigger. The designer only saves this node in a Mailhook workflow, which is exactly what you have. Configure it to grab the form:

  • Mode: Single so you get one attachment back as an object.
  • Content type: application/pdf.
  • Filename pattern: a glob such as *.pdf (or new-hire-*.pdf if your form has a stable name).
  • Fail if no attachment matches: turn this on so an email without a form stops the run instead of creating an empty employee.

The node outputs { filename, contentType, size, content }, where content is the base64-encoded PDF. Name the output something like form so you can reference {{ form.content }} downstream. Attachments are limited to 10 MB each and 25 MB per run by default.

Step 3: Read the form text with the pdf connector

Add a Connector node in Direct mode and pick the pdf connector with the extract-text tool. Map its document input to the base64 attachment from the previous step, {{ form.content }}. Direct mode is deterministic and costs no AI credits, which makes it the right choice for a single predictable call. Name the output formText; the extracted plain text becomes available as {{ formText.text }} for the extraction step.

If your forms are scanned images rather than digital PDFs, extract-text will return little or no text. In that case, embed the document into a Knowledge node in Embed mode with Document Type set to Images via OCR, then query it in the same run using a Transient collection. For standard digital PDFs, extract-text is faster and cheaper.

Step 4: Extract the fields with a Connector node in Agent mode

Add a Connector node and switch it to Agent mode. Agent mode lets Miraxa, the intelligent layer across your automation, read the messy form text and return clean fields. Define a Response Schema so the output is reliable JSON rather than prose:

{
  "type": "object",
  "properties": {
    "firstName": { "type": "string" },
    "lastName":  { "type": "string" },
    "email":     { "type": "string" },
    "mobile":    { "type": "string" },
    "startDate": { "type": "string" },
    "position":  { "type": "string" },
    "location":  { "type": "string" }
  },
  "required": ["firstName", "lastName", "email"]
}

Use a prompt such as: Extract the new hire details from this form text and return them in the response schema. Form text: {{ formText.text }}. Name the output hire, giving you {{ hire.firstName }}, {{ hire.email }}, {{ hire.startDate }}, and the rest for later steps.

Step 5: Pause for manager approval with a Human node

Add a Human node so no employee is created without sign-off. Configure it so the approver sees exactly who is about to be added:

  • Label: Approve new hire.
  • Message: Create Deputy employee for {{ hire.firstName }} {{ hire.lastName }} ({{ hire.email }}), starting {{ hire.startDate }}?
  • Approval slots: add the hiring manager as a User atom, or a Role/Team atom so any member can approve. Approval completes only when every slot is satisfied.
  • Timeout (minutes): set a window such as 2880 (two days) if you want stale requests to auto-close. A timeout is treated as a rejection.
  • Email approvers: turn on if your managers do not watch the in-app Approvals inbox.

On approval the node outputs { approved: true, approvalId, outcome: "APPROVED" } and the run continues. A rejection or timeout halts the workflow, so nothing is written to Deputy. Branching on rejection is not supported, which is the safe default here.

Step 6: Create the employee in Deputy

Add a Connector node in Direct mode, choose the Deputy connector, and select the create-employee tool. Build the employee object from the extracted fields. Deputy expects capitalized field names:

{
  "FirstName": "{{ hire.firstName }}",
  "LastName":  "{{ hire.lastName }}",
  "Email":     "{{ hire.email }}",
  "Mobile":    "{{ hire.mobile }}"
}

Name the output employee. Use the response (for example the new employee Id) in the next step's confirmation message. Keep this in Direct mode: it is a single, deterministic write that should never be left to a model's discretion.

Step 7: Notify HR in Slack

Finish with a Connector node in Direct mode using the Slack connector and the send-message tool. Set the channel to your HR channel and the text to a clear confirmation, for example: New hire created in Deputy: {{ hire.firstName }} {{ hire.lastName }} ({{ hire.email }}), starting {{ hire.startDate }}. If you would rather acknowledge the submitter, add a Send Email node addressed to {{ input.replyTo }} instead of or alongside the Slack post, since a mailhook never replies to the sender on its own.

Tips

  • Scaffold the whole flow fast by asking Miraxa: Build a workflow that watches a mailhook, fetches the PDF attachment, extracts new-hire fields with an Agent mode node, requires manager approval, then creates a Deputy employee and posts to Slack. Then fine-tune each node in the properties panel.
  • Use the Subject regex and From allowlist on the trigger to reject newsletters and replies before they ever reach the extraction step, saving AI credits.
  • Make the Response Schema fields you actually rely on required, so a form missing an email surfaces as an extraction failure rather than a half-built Deputy record.
  • If forms sometimes carry multiple attachments (form plus ID scan), set the Attachment node Filename pattern narrowly so it matches only the new-hire form.

Common Pitfalls

  • Saving an Attachment node in a workflow whose trigger is not a Mailhook: the designer refuses it. The trigger must be Mailhook.
  • Scanned (image-only) PDFs return empty text from extract-text. Switch to a Knowledge node with Images via OCR for those, or ask HR to send digital PDFs.
  • Wrong Deputy field casing. The create-employee tool expects FirstName, LastName, and Email (capitalized), not the lowercase keys from your extraction schema.
  • Assuming a rejected approval can branch to a fallback. A rejected or timed-out Human node halts the run, so put nothing critical after it that should also handle the reject case.
  • Rotating the mailhook with Regenerate address kills the old address instantly. Update HR's forwarding rule before regenerating.

Testing

Before pointing real HR mail at the address, forward one sample new-hire form to the generated mailhook address yourself. Watch the run appear in the execution history within seconds, then confirm the Attachment node returned the PDF, the extract-text output has readable text, and the Agent mode node produced the expected {{ hire.* }} fields. Approve the request from the Approvals inbox and verify the employee shows up in Deputy and the Slack message posts. Once a clean form passes end to end, test a deliberately bad email (no attachment) to confirm Fail if no attachment matches stops the run cleanly. Then share the address with HR.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.