Auto-send Bullhorn offer letters through Docusign each morning

Every weekday at 7am, turn every newly verbal-accepted placement in Bullhorn into a sent Docusign offer letter, with a Slack ping for the recruiter.

Agentic Task
BullhornDocusignSlack BotHR & PeopleOperationsOnboarding AutomationNotifications & AlertsDocument Processing

Every weekday at 7am in our local time zone, I want an agent to sweep Bullhorn for placements that just moved into a Verbal Accepted (or equivalent pre-offer) status in the last 24 hours, turn each one into a sent Docusign offer letter, and ping the owning recruiter in Slack. The agent picks the template language and recipient routing per placement, which is why this should be an agent workflow rather than code. The trigger is cron, weekdays at 7am.

Step 1. Use Bullhorn Search Entities on the Placement entity with a Lucene query that filters for status changes in the last 24 hours into any of these statuses: Verbal Accepted, Pending Offer, Ready for Offer, Offer Pending. Return placement IDs only. Skip any placement that already has a Docusign envelope ID written to its custom field (so we never resend).

Step 2. For each matching placement, call Bullhorn Get Entity by ID and pull the full nested record: the placement itself (status, salary or hourly rate, start date, employment type / classification, owning recruiter), the linked Candidate (legal name, email, phone), the linked ClientContact (hiring manager name and email), and the linked JobOrder (title, client company name, location, employment type if not on placement). Use nested field selection so all of this comes back in one call per placement.

Step 3. Draft a clean offer letter body customized for that placement. The agent should adjust language for the employment classification: permanent (annualized salary, benefits eligibility, at-will), contract (hourly or daily rate, end date or duration, contractor classification), or contract-to-hire (initial contract terms plus conversion target). It should also tone-shift for seniority based on the job title. Keep it one page, plain prose, no legalese the agent invents on its own. Include: candidate legal name, role title, client company, start date, comp (formatted correctly for the type), reporting manager, and a signature block for both candidate and hiring manager.

Step 4. Call Docusign Create Envelope with that letter as the document. Set status to "sent" so it goes out immediately. Recipients: the candidate as signer 1 (routing order 1) and the hiring manager (ClientContact) as signer 2 (routing order 2). Place signHere and dateSigned tabs for each signer in the signature block. Subject and email body should reference the role title and client company.

Step 5. Find the owning recruiter in Slack by their email (from the Bullhorn placement owner). Use Slack Bot Look Up User by Email, then Open a Conversation to get their DM channel ID, then Send a Message containing: the candidate name and role, the client company, the comp, the start date, the envelope tracking link from Docusign, and a one-paragraph plain-English summary of the offer terms the agent just sent. If the recruiter cannot be matched in Slack, fall back to a shared #recruiting-offers channel.

Step 6. Call Bullhorn Update Entity on the Placement to write the Docusign envelope ID onto a tracking field (for example customText1 or a dedicated envelope ID field if one exists) so tomorrow's run can recognize this placement as already processed, and so we can later reconcile signed status back to Bullhorn.

Error handling: if a placement is missing required fields (no candidate email, no hiring manager, no comp, no start date), skip the Docusign step and instead DM the owning recruiter in Slack with a checklist of what is missing on that placement. Never send a half-filled offer letter.

Additional information

What does this prompt do?
  • Watches Bullhorn each weekday morning for placements that just moved into a verbal acceptance status in the last 24 hours.
  • Pulls the full placement context (candidate, hiring manager, job title, comp, start date, and whether the role is perm, contract, or contract-to-hire) so the offer letter reads correctly for each situation.
  • Drafts a clean, role-appropriate offer letter and sends it through Docusign with the candidate and hiring manager already lined up as signers.
  • Posts the envelope tracking link and a short summary of the offer terms straight to the owning recruiter in Slack, and writes the envelope ID back onto the Bullhorn placement so you can reconcile later.
What do I need to use this?
  • A Bullhorn account with API access to read and update Placement, Candidate, ClientContact, and JobOrder records.
  • A Docusign account that can create and send envelopes from your workspace.
  • A Slack workspace where the General Input bot can DM your recruiters by email.
How can I customize it?
  • Change the run time or cadence (for example, twice a day, or only Tuesday through Friday).
  • Adjust which Bullhorn statuses count as ready to offer (verbal accepted, pending paperwork, ready for offer, and so on).
  • Edit the offer letter wording for each employment type, or swap in different language for senior roles.
  • Route the Slack notification to a shared recruiting channel instead of the recruiter's DM.

Frequently asked questions

Why a 7am cron sweep instead of reacting to status changes live?
Bullhorn does not send reliable outbound notifications for status changes, so a scheduled morning sweep is the realistic way to catch every placement that flipped to verbal accepted the day before.
Does this send the offer letter without anyone reviewing it?
By default yes, but you can ask the workflow to post the draft to Slack for sign-off first and only send the envelope after the recruiter approves.
How does it know which letter language to use?
It reads the placement's employment classification from Bullhorn (permanent, contract, or contract-to-hire) and picks the template wording that matches, including the seniority of the role.
What stops it from sending the same letter twice?
After Docusign accepts the envelope, the workflow writes the envelope ID back onto the placement record in Bullhorn, so the next morning's sweep can skip anything that already has one.
Can I send from my own Docusign templates instead of a fresh document?
Yes. Point the workflow at one of your existing Docusign templates per employment type, and it will use those instead of building the letter from scratch.

Stop hand-building offer letters every morning.

Connect Bullhorn, Docusign, and Slack once, and let Geni run the offer letter pipeline for you before the day starts.