Overview
GLYDE Unity provides the most comprehensive ATS integration with Bullhorn, the staffing and recruiting industry's leading CRM and ATS platform. This integration supports candidates, jobs, applications, notes, activities, email messaging, Lucene and JPQL search, event polling, and full ATS proxy discovery — making it the richest connector in the GLYDE ecosystem.
The integration spans the full GLYDE platform: Microsoft Teams, Copilot for Microsoft 365,
Power Automate, the GLYDE Unity SDK, the Model Context Protocol (MCP) for AI agents, and the
GLYDE Voice Agent. All operations are tenant-isolated using client_uuid and
authenticated via a 4-tier OAuth2 strategy with automatic data center resolution
and BhRestToken management.
Bullhorn data flows through GLYDE Unity's index-centric normalization pipeline,
converting Bullhorn's camelCase field naming and epoch-millisecond dates to GLYDE's canonical
snake_case format. The adaptor automatically routes queries between Lucene
(searchEntities) for indexed entities and JPQL (queryOrFindEntities)
for non-indexed entities.
Architecture & Data Flow
GLYDE Unity acts as an orchestration layer between consuming platforms (Teams, Copilot, Power Platform, SDK) and the Bullhorn REST API. Requests are authenticated, normalized, and routed through the integration adaptor's 8 entity sub-adaptors, which handle entity-specific business logic, data mapping, and dual query syntax (Lucene for indexed entities, JPQL for non-indexed entities).
Teams / Copilot / Power Platform / SDK / MCP Client
|
v
┌─────────────────────────────────────┐
│ GLYDE Unity REST API │
│ api.glydeunity.com/api/unity/* │
│ │
│ ┌─ Auth Middleware ─────────────┐ │
│ │ API Key / JWT / Trusted App │ │
│ │ Role-based access control │ │
│ └──────────────────────────────┘ │
│ │
│ ┌─ Workflow Route Handler ──────┐ │
│ │ Validation + Orchestration │ │
│ └──────────────────────────────┘ │
│ | │
│ v │
│ ┌─ Bullhorn Integration Adaptor ─┐ │
│ │ 8 Entity Sub-Adaptors: │ │
│ │ candidates / applications / │ │
│ │ search / messages / activities│ │
│ │ activities-unified / notes │ │
│ │ │ │
│ │ Auto-Routing: Lucene vs JPQL │ │
│ │ Template Method Pattern │ │
│ │ Index-centric Data Mapping │ │
│ └────────────────────────────────┘ │
│ | │
│ v │
│ ┌─ Bullhorn External Connector ──┐ │
│ │ bullhorn-connector.ts │ │
│ │ 16 OpenAPI Operations │ │
│ │ BullhornConnectorAuthManager │ │
│ │ 4-Tier OAuth2 + Data Center │ │
│ │ Resolution (loginInfo) │ │
│ └────────────────────────────────┘ │
└──────────────|──────────────────────┘
|
v
┌──────────────────────────────────────┐
│ Bullhorn REST API │
│ rest{DC}.bullhornstaffing.com │
│ /{corpToken}/... │
│ │
│ Candidate | JobOrder | JobSubmission │
│ Note | Placement | ClientContact │
│ ClientCorporation | Events │
└──────────────────────────────────────┘
Teams, Copilot, Power Automate, or SDK sends a request to the GLYDE Unity API (e.g., POST /api/unity/candidates) with API key + user context headers.
Unity validates the API key, resolves client_uuid and user_uuid, checks role-based access, and routes to the appropriate workflow handler.
The workflow handler calls getIntegrationAdaptor('bullhorn') to obtain the Bullhorn adaptor based on the client's configured ats_integration_type.
The BullhornConnectorAuthManager automatically authenticates using a 4-tier strategy: (1) reuse existing BhRestToken, (2) OAuth2 refresh token flow, (3) programmatic OAuth with username/password, (4) user-initiated OAuth redirect. Data center is auto-resolved via the loginInfo endpoint (24-hour cache).
The BullhornConnector executes the operation against the Bullhorn REST API using the OpenAPI specification (16 operations across 13 paths). The dynamic rest_url includes the corpToken and data-center-specific hostname.
Response data flows through the index-centric pipeline: ATS response → CandidateDataMapper → ATSCacheCandidateSearchIndex (canonical format) → searchIndexToCandidate() → CRUD schema. Dates convert from epoch milliseconds to ISO strings; fields convert from camelCase to snake_case.
The normalized response is returned to the consuming platform in the GLYDE Unity standard response format, with consistent field names and structures regardless of Bullhorn's native conventions.
Authentication
OAuth2 with 4-Tier Authentication Strategy
Bullhorn uses a multi-tier OAuth2 authentication flow. GLYDE Unity's
BullhornConnectorAuthManager (extending BaseOAuth2AuthManager)
handles the complete token lifecycle automatically with four fallback tiers:
- Tier 1: Reuse existing
bh_rest_tokenif available and not expired - Tier 2: OAuth2 refresh token flow — exchange
refresh_tokenfor newaccess_token, then exchange forBhRestTokenvia/rest-services/login - Tier 3: Programmatic OAuth with
username/password— calls/oauth/authorizewithaction=Login(no browser redirect) - Tier 4: User-initiated OAuth redirect — builds authorization URL and throws
ATSOAuthRequiredErrorfor UI-driven authentication
Credential Fields
| Field | Description | Required |
|---|---|---|
client_id |
Bullhorn OAuth2 client ID for API access | Yes |
client_secret |
Bullhorn OAuth2 client secret for token exchange | Yes |
username |
Bullhorn API username for programmatic OAuth (Tier 3) | No |
password |
Bullhorn API password for programmatic OAuth (Tier 3) | No |
refresh_token |
OAuth2 refresh token for Tier 2 authentication | No |
bh_rest_token |
Pre-existing BhRestToken for Tier 1 authentication | No |
rest_url |
Dynamic REST URL including corpToken (data-center-specific, e.g., https://rest91.bullhornstaffing.com/rest-services/arpw2p/) | No |
data_center |
Bullhorn data center identifier (auto-resolved via loginInfo if not provided) | No |
Session Management
Sessions are cached in Redis with the key pattern auth:session:{clientUuid}:bullhorn.
The session includes the BhRestToken, dynamic rest_url (with corpToken),
expiration timestamp, refresh token, access token, data center identifier, and resolved auth URL.
Sessions are automatically invalidated when the BhRestToken expires and re-authenticated on the next request.
Token Refresh
The BullhornConnectorAuthManager validates the BhRestToken before each API call
by checking the rest_token_expires_at field or parsing the JWT exp claim.
If expired, Tier 2 (refresh token) is attempted first, falling back to Tier 3 (programmatic OAuth).
The data center is resolved via https://rest.bullhornstaffing.com/rest-services/loginInfo?username=...
and cached in Redis with a 24-hour TTL. The auth URL is derived from the data center:
https://auth-{dataCenter}.bullhornstaffing.com.
Capabilities
| Operation | Endpoint | ATS Op |
|---|---|---|
| Create Candidate Create a new Candidate entity in Bullhorn with GLYDE DB sync and search indexing |
POST /api/unity/candidates |
createOrUpdateEntity (Candidate) |
| Get Candidate by UUID Retrieve candidate by GLYDE UUID via ExternalMapping lookup, then Bullhorn getEntityById |
GET /api/unity/candidates/:candidateUUID |
getEntityById (Candidate) |
| Get Candidate by ATS ID Retrieve candidate directly by Bullhorn numeric ID |
GET /api/unity/candidates/:candidateUUID?ats_candidate_id=&ats=bullhorn |
getEntityById (Candidate) |
| Search Candidates Search candidates using Lucene query syntax via searchEntities |
POST /api/unity/search/aiSearchIndex |
searchEntities (Candidate) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| Create Job Create a new JobOrder entity in Bullhorn |
POST /api/unity/jobs |
createOrUpdateEntity (JobOrder) |
| Update Job Update an existing JobOrder entity |
PUT /api/unity/jobs/:jobUUID |
updateEntity (JobOrder) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| List Applications by ATS Candidate ID List all JobSubmission records for a Bullhorn candidate using JPQL query |
GET /api/unity/applications/candidate/:atsCandidateId |
queryOrFindEntities (JobSubmission) |
| Update Application Create or update a JobSubmission entity in Bullhorn |
PUT /api/unity/applications/:applicationUUID |
createOrUpdateEntity (JobSubmission) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| Send Email Send email via Mailgun with automatic Note creation in Bullhorn for activity logging |
POST /api/unity/messages/send-email |
Mailgun + createOrUpdateEntity (Note) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| Create Activity Create a Note entity in Bullhorn mapped from a GLYDE activity, with action type classification |
POST /api/unity/activities |
createOrUpdateEntity (Note) |
| Get Activities Retrieve Note entities from Bullhorn with filtering by action type, date range, and entity scope |
GET /api/unity/activities/:entityType/:entityId |
queryOrFindEntities (Note) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| Create Candidate Note Create a Note entity linked to a candidate with optional job association |
POST /api/unity/candidates/:candidateUUID/notes |
createOrUpdateEntity (Note) |
| Get Candidate Notes Retrieve all Note entities for a candidate via JPQL query |
GET /api/unity/candidates/:candidateUUID/notes |
queryOrFindEntities (Note) |
| Operation | Endpoint | ATS Op |
|---|---|---|
| ATS Search (Lucene) Search indexed Bullhorn entities using Lucene query syntax via searchEntities |
POST /api/unity/search/aiSearchIndex |
searchEntities |
| Conversational AI Search Natural language search across candidates and jobs using GLYDE AI with Azure AI Search |
POST /api/unity/search/conversationalAISearch |
Azure AI Search Index (not direct ATS) |
| ATS Proxy Search Direct search or query against Bullhorn API via ATS proxy (Lucene or JPQL) |
POST /api/unity/proxy/bullhorn/searchEntities |
searchEntities or queryOrFindEntities |
| Operation | Endpoint | ATS Op |
|---|---|---|
| Discover Operations List all 16 available Bullhorn API operations with schemas, parameters, and LLM metadata |
GET /api/unity/proxy/discover/bullhorn |
OpenAPI spec introspection |
| Execute Operation Execute any discovered Bullhorn operation dynamically with auto-authentication |
POST /api/unity/proxy/bullhorn/:operationId |
Dynamic (per operationId) |
| List Entity Types List all available Bullhorn entity types |
GET /api/unity/proxy/meta/bullhorn |
getEntityMetadata |
| Entity Field Metadata Get field-level metadata for a Bullhorn entity type including field names, types, required status, and picklist options |
GET /api/unity/proxy/meta/bullhorn/:entity |
getEntityMetadata |
| Picklist Options Retrieve picklist/dropdown options for a specific entity field |
GET /api/unity/proxy/bullhorn/getPicklistOptions |
getPicklistOptions |
Platform Access
GLYDE Unity provides unified access to Bullhorn across multiple Microsoft 365 and developer platforms.
Access Bullhorn data through the GLYDE Unity bot in Teams channels and chats.
Access: Teams Bot Framework → GLYDEBuddy Proxy → Unity REST API → Bullhorn Adaptor
- Search candidates and jobs via conversational AI
- View candidate profiles with Bullhorn data
- Send emails with automatic Bullhorn Note logging
- Create and manage candidate notes
- Receive event-driven notifications for Bullhorn changes
- Access ATS proxy for advanced Bullhorn queries
Use natural language to interact with Bullhorn data through the GLYDE Copilot declarative agent.
Access: Copilot Agent → MCP Bridge (GLYDEBuddy) → Unity REST API → Bullhorn Adaptor
- Natural language queries ("Find Java developers in Chicago in Bullhorn")
- AI-powered candidate matching and ranking
- Multi-step workflows (search → review → contact)
- ATS proxy with LLM-optimized operation metadata
- Entity and field metadata inspection for schema discovery
Automate Bullhorn workflows using the GLYDE Unity Power Platform connector.
Access: Power Platform Connector → Unity REST API → Bullhorn Adaptor
- Trigger flows on Bullhorn events (JobSubmission, JobOrder changes)
- Create candidates from forms or external sources
- Sync data between Bullhorn and other business systems
- Build custom Power Apps for recruiter workflows
- Schedule bulk operations and reports
Programmatic access via the @glydeunity/glyde-api-client npm package.
Access: SDK Client → Unity REST API → Bullhorn Adaptor
- Type-safe TypeScript API client with Zod validation
- FlexibleUnityApi for multiple input formats (UUID, ATS ID, object)
- Direct workflow operations and ATS proxy access
- Automatic authentication and session management
- OpenAPI-generated client with full IntelliSense
AI agents and LLMs access Bullhorn operations through the MCP interface.
Access: MCP Client → Unity MCP Endpoints → Workflow Handlers → Bullhorn Adaptor
- 100+ discoverable tools auto-generated from workflow routes
- ATS proxy tools for dynamic Bullhorn operation discovery and execution
- Entity metadata tools for field-level schema inspection
- LLM metadata with query syntax guidance and anti-pattern documentation
- JSON-RPC and REST invocation protocols
Voice-based interaction with Bullhorn data for candidate screening and discovery.
Access: Voice SDK → Unity Voice API → Deepgram → Function Execution → Bullhorn Adaptor
- Voice-powered candidate screening interviews
- Job discovery and application via voice
- Real-time candidate creation during conversations
- OTP verification for candidate identity
- Context-aware conversation modes (screening, discovery, application)
Entity Mappings
Bullhorn uses different entity names and field conventions. GLYDE Unity normalizes these through the index-centric data pipeline.
Candidate → Candidate
| Unity Field | Bullhorn Field | Direction | Notes |
|---|---|---|---|
first_name |
firstName |
Bidirectional | Required |
last_name |
lastName |
Bidirectional | Required |
email |
email |
Bidirectional | Required; used for deduplication |
phone |
phone |
Bidirectional | — |
mobile |
mobile |
Bidirectional | — |
city |
address.city |
Bidirectional | Address is a sub-object in Bullhorn (not top-level fields) |
state |
address.state |
Bidirectional | — |
zip |
address.zip |
Bidirectional | — |
date_of_birth |
dateOfBirth |
Bidirectional | Epoch milliseconds in Bullhorn; ISO string in GLYDE |
status |
status |
Read | — |
candidate_uuid |
id (via ExternalMapping) |
Read | Mapped through GLYDE ExternalMapping table |
Job → JobOrder
| Unity Field | Bullhorn Field | Direction | Notes |
|---|---|---|---|
title |
title |
Bidirectional | — |
description |
description |
Bidirectional | — |
status |
status |
Bidirectional | — |
employment_type |
employmentType |
Bidirectional | — |
salary_min |
salary |
Bidirectional | Maps via JobDataMapper |
salary_max |
salaryUnit |
Bidirectional | — |
Application → JobSubmission
| Unity Field | Bullhorn Field | Direction | Notes |
|---|---|---|---|
application_uuid |
id |
Read | — |
candidate_uuid |
candidate.id |
Write | Uses nested reference format candidate(id) |
job_uuid |
jobOrder.id |
Write | Uses nested reference format jobOrder(id) |
status |
status |
Read | — |
Note → Note
| Unity Field | Bullhorn Field | Direction | Notes |
|---|---|---|---|
content |
comments |
Bidirectional | — |
action |
action |
Bidirectional | Action type string (e.g., "Note", "Phone Call", "Email") |
created_at |
dateAdded |
Read | Epoch milliseconds in Bullhorn |
candidate_id |
personReference.id |
Write | Links note to candidate entity |
job_id |
jobOrder.id |
Write | Optional; links note to job entity |
Activity → Note (via BullhornActivityMapper)
| Unity Field | Bullhorn Field | Direction | Notes |
|---|---|---|---|
subtype: phone_call |
action: "Candidate Phone Call" or "Phone Call" |
Bidirectional | — |
subtype: email_sent |
action: "Email" or "Emailed Candidate" |
Bidirectional | — |
subtype: interview_scheduled |
action: "Interview Scheduled" |
Bidirectional | — |
subtype: interview_completed |
action: "Interview" |
Bidirectional | — |
subtype: offer_extended |
action: "Offer" |
Bidirectional | — |
subtype: reference_check |
action: "Reference" |
Bidirectional | — |
subtype: background_check |
action: "Background Check" |
Bidirectional | — |
subtype: follow_up |
action: "Follow Up" or "Follow-up" |
Bidirectional | — |
subtype: general_note |
action: "General Note" or "Note" |
Bidirectional | — |
subtype: general_contact |
action: "Contact" |
Bidirectional | — |
Endpoint Reference
| Method | Unity Endpoint | Description | ATS Operation | Auth |
|---|---|---|---|---|
POST |
/api/unity/candidates |
Create candidate | createOrUpdateEntity (Candidate) |
API Key + User UUID |
GET |
/api/unity/candidates/:uuid |
Get candidate by ID | getEntityById (Candidate) |
API Key + User UUID |
POST |
/api/unity/jobs |
Create job | createOrUpdateEntity (JobOrder) |
API Key + User UUID |
PUT |
/api/unity/jobs/:uuid |
Update job | updateEntity (JobOrder) |
API Key + User UUID |
GET |
/api/unity/applications/candidate/:atsCandidateId |
List applications by ATS candidate ID | queryOrFindEntities (JobSubmission) |
API Key + User UUID |
PUT |
/api/unity/applications/:uuid |
Update application | createOrUpdateEntity (JobSubmission) |
API Key + User UUID |
POST |
/api/unity/messages/send-email |
Send email + log Bullhorn Note | Mailgun + createOrUpdateEntity (Note) |
API Key + User UUID |
POST |
/api/unity/activities |
Create activity (Bullhorn Note) | createOrUpdateEntity (Note) |
API Key + User UUID |
GET |
/api/unity/activities/:entityType/:entityId |
Get activities for entity | queryOrFindEntities (Note) |
API Key + User UUID |
POST |
/api/unity/candidates/:uuid/notes |
Create candidate note | createOrUpdateEntity (Note) |
API Key + User UUID |
GET |
/api/unity/candidates/:uuid/notes |
Get candidate notes | queryOrFindEntities (Note) |
API Key + User UUID |
POST |
/api/unity/search/aiSearchIndex |
ATS search (Lucene) | searchEntities |
API Key + User UUID |
POST |
/api/unity/search/conversationalAISearch |
AI-powered search | Azure AI Search |
API Key + User UUID |
GET |
/api/unity/proxy/discover/bullhorn |
Discover ATS operations | OpenAPI introspection |
API Key + User UUID |
POST |
/api/unity/proxy/bullhorn/:operationId |
Execute ATS operation | Dynamic |
API Key + User UUID |
GET |
/api/unity/proxy/meta/bullhorn |
List entity types | getEntityMetadata |
API Key + User UUID |
GET |
/api/unity/proxy/meta/bullhorn/:entity |
Get entity field metadata | getEntityMetadata |
API Key + User UUID |
Events & Webhooks
Bullhorn supports subscription-based event polling via the BullhornEventPoller.
GLYDE Unity creates event subscriptions for specified entity types and event types, then polls
the /event/events endpoint for new events using sequence-based pagination.
Events are normalized to the canonical EventObject format via the
BullhornEventNormalizer.
Subscription Management
Subscriptions are created via PUT /event/subscription/{subscriptionId} with configurable
entity names and event types. The poller gracefully handles 409 (subscription exists) and 404
(subscription not found, triggers recreation).
Event Polling
Events are retrieved via GET /event/events with startSequence for
pagination. The sequence is extracted from eventId or requestId in
the response. Events are dispatched to handlers for processing (e.g., syncing changes to GLYDE DB).
| Event | Trigger | Payload |
|---|---|---|
JobSubmission.INSERTED |
When a new JobSubmission is created in Bullhorn | entityId, entityName, eventType, timestamp, eventMetadata |
JobSubmission.UPDATED |
When a JobSubmission is modified in Bullhorn | entityId, entityName, eventType, updatedProperties[], timestamp |
JobSubmission.DELETED |
When a JobSubmission is removed in Bullhorn | entityId, entityName, eventType, timestamp |
JobOrder.INSERTED |
When a new JobOrder is created in Bullhorn | entityId, entityName, eventType, timestamp, eventMetadata |
JobOrder.UPDATED |
When a JobOrder is modified in Bullhorn | entityId, entityName, eventType, updatedProperties[], timestamp |
JobOrder.DELETED |
When a JobOrder is removed in Bullhorn | entityId, entityName, eventType, timestamp |
Configuration Requirements
-
OAuth2 Client CredentialsREQUIRED
Bullhorn issues client_id and client_secret for API access. These must be stored in Azure Key Vault via the GLYDE Unity client configuration.
client_id: "glyde-bullhorn-client" -
OAuth2 Username/Password (Tier 3)
For programmatic OAuth authentication without browser redirect. Required only when refresh tokens are unavailable. Stored in Azure Key Vault.
username: "api_user", password: "********" -
Data Center
Bullhorn data center identifier (e.g., "rest91"). Auto-resolved via the loginInfo endpoint if not pre-configured. Cached in Redis with 24-hour TTL.
data_center: "rest91" -
Commenting Person IDREQUIRED
The Bullhorn user ID used as the author (commentingPerson) for GLYDE-created Note entities. Configured in the client's integration configuration.
commentingPerson: 12345 -
Integration TypeREQUIRED
The client's integration_type must be set to "bullhorn" in the GLYDE client configuration for the adaptor router to select the correct integration.
integration_type: "bullhorn" -
Event Subscription Configuration
Entity types and event types for the BullhornEventPoller subscription. Defaults to JobSubmission and JobOrder with INSERTED, UPDATED, DELETED events.
entityNames: "JobSubmission,JobOrder", eventTypes: "INSERTED,UPDATED,DELETED" -
REST Token (Pre-supplied)
An existing BhRestToken can be pre-supplied for Tier 1 authentication, bypassing OAuth entirely. Typically used for quick testing or migration.
bh_rest_token: "abc123-rest-token" -
REST URL (Pre-supplied)
A pre-configured dynamic REST URL including corpToken. If not provided, resolved automatically during authentication.
rest_url: "https://rest91.bullhornstaffing.com/rest-services/arpw2p/"
Known Limitations
- Indexed vs Non-Indexed Entity Routing: Candidate, JobOrder, ClientContact, ClientCorporation, Lead, and Opportunity are indexed and require
searchEntities(Lucene syntax). All other entities (JobSubmission, Placement, Note, etc.) requirequeryOrFindEntities(JPQL syntax). Never mix query syntaxes. The adaptor auto-routes when possible. - Lucene and JPQL Syntax Must Not Be Mixed:
searchEntitiesuses Lucene (field:value,field:[start TO end]).queryOrFindEntitiesuses JPQL (field = 'value',field > value). OData operators (ge,le,gt,lt,eq,ne) are never supported. - searchEntities Prunes Nested Field Projections: The
fieldsparameter forsearchEntitiesonly accepts scalar field names. Nested field syntax likecandidate(id,firstName)is automatically pruned by the connector. UsequeryOrFindEntitieswith nested fields for non-indexed entities. - TO_MANY Associations Cannot Be Direct Fields: Fields like
skills,certifications,education, andexperienceare TO_MANY associations in Bullhorn and cannot be requested as direct fields insearchEntitiesorqueryOrFindEntities(returns 400). Use separate entity queries to retrieve associated data. - Dates Are Epoch Milliseconds: All date fields in Bullhorn are stored as epoch milliseconds (e.g.,
1707523200000), not ISO strings. JPQL date comparisons use epoch ms directly. Lucene date ranges can useyyyyMMddformat or epoch ms. The mapper converts to ISO strings for GLYDE consumers. - orderBy Accepts Property Name Only: The
orderByparameter forqueryOrFindEntitiesaccepts only the property name (e.g.,dateAdded). Do NOT include ASC/DESC suffixes. ForsearchEntities, use thesortparameter with-fieldprefix for descending. - Job Operations Live in Candidates Adaptor:
createJob()andupdateJob()are implemented inBullhornAdaptorCandidates, not in a separate Jobs adaptor class. This is a structural quirk, not a functional limitation. - listCandidateHotlistsByAtsId() Not Implemented: The Tearsheet (hotlist) retrieval operation returns
false(not supported) in the current implementation. - Address Field Returns Object: Bullhorn returns the
addressfield as a full sub-object withcity,state,zip,country,latitude,longitude. The mapper handles both object and JSON string formats for compatibility. - Nested Field Syntax Uses Parentheses: Bullhorn uses parenthesis notation for nested fields:
candidate(id,firstName), NOT dot notation (candidate.id). This applies to bothfieldsprojection and association traversal.
Version History
- Documented default recruiter ownership behavior for write operations using recruiter ExternalMappings (type=recruiter)
- Documented note creation default for commentingPerson from mapped recruiter external_ref when available
- Initial integration guide creation
- Documented 4-tier OAuth2 authentication flow with data center resolution
- Documented candidate CRUD operations with index-centric data mapping
- Documented job operations (create, update) via BullhornAdaptorCandidates
- Documented application operations (list by candidate, update)
- Documented email messaging with automatic Bullhorn Note logging
- Documented unified activities and notes CRUD with BullhornActivityMapper
- Documented Lucene and JPQL search with auto-routing for indexed entities
- Documented subscription-based event polling via BullhornEventPoller
- Documented ATS proxy discovery with 16 operations and LLM metadata
- Documented platform access patterns (Teams, Copilot, Power Platform, SDK, MCP, Voice)
- Documented entity mappings: Candidate, JobOrder, JobSubmission, Note, Activity
- Documented 10 known limitations including query syntax constraints