Skip to main content

Implementation Overview

Implementation Type: Minimal Implementation (Webhook-based)
Note Format: N/A
Status: Production Ready
The Public API integration is not a traditional ATS integration. Instead, it provides a minimal ATS client implementation that supports webhook-based integrations for organizations using Tenzo’s Public API rather than a traditional ATS system.

Configuration

Feature Coverage

Streaming Operations

Stream Jobs

Status: Not Supported
Method: _stream_jobs(updated_after, statuses)
Not supported. Jobs are provided via Public API and stored in AtsJobCache. No streaming needed.

Stream Applications

Status: Not Supported
Method: _stream_applications(updated_after, for_job_ids)
Not supported. Applications are created via Public API calls.

Stream Candidates

Status: Not Supported
Method: _stream_candidates(updated_after)
Not supported. Candidates are provided via Public API.

Move Application to Stage

Status: Not Supported
Method: _move_application_to_stage(application, stage)
Not applicable - stage management handled externally.

Get Disposition Reasons

Status: Not Supported
Method: get_disposition_reasons()
Not applicable for webhook integration.

Reject Application

Status: Not Supported
Method: reject_application(application_id, reason_id)
Not applicable - application management handled externally.

Bulk Reject Applications

Status: Not Supported
Method: bulk_reject_applications(application_ids, reason_id)
Not applicable.

Create Application

Status: Supported (via webhook service)
Method: _create_application_for_candidate(candidate, job)
Applications are created when customers submit data via the Public API. The integration stores minimal data and sends results via webhooks.

Get All Application Stages

Status: Not Supported
Method: _get_all_application_stages()
Not applicable.

Get Job by ID

Status: Supported
Method: get_job_by_job_id(job_id)
Implementation Notes:
Retrieves jobs from AtsJobCache that were created via the Public API. Does not query an external ATS.

Get Enhanced Job

Status: Supported (Basic)
Method: get_enhanced_job(kombo_id, remote_job_id)
Returns cached job data without additional enhancement. No external ATS to query.

Fetch Enhanced Jobs Batch

Status: Not Supported
Method: _fetch_enhanced_jobs_batch_from_ats(limit, cursor)
Not applicable - jobs come from Public API, not streaming.

Get Candidate by ID

Status: Supported
Method: _get_candidate_by_id(candidate_id)
Implementation Notes:
Uses temporary candidate storage (_temp_candidate) for processing. Candidate data is provided via Public API.

Find Candidates by Details

Status: Not Supported
Method: _find_candidates_by_details(first_name, last_name, email, phone)
Not applicable - candidates provided via API, not searched.

Get Resume

Status: Supported
Method: _get_resume_for_candidate_id(candidate_id)
Implementation Notes:
Uses temporary resume storage (_temp_resume) for processing. Resume data provided via Public API.

Add Note to Application

Status: Not Supported
Method: _add_note_to_application(application, note, note_action_type)
Not applicable - no external ATS to write to.

Add Note to Candidate

Status: Not Supported
Method: _add_note_to_candidate(candidate, note, note_action_type)
Not applicable.

Add Attachment to Application

Status: Not Supported
Method: _add_attachment_to_application(filename, application_id, pdf_b64)
Not applicable.

Add Attachment to Candidate

Status: Not Supported
Method: _add_attachment_to_candidate(filename, candidate_id, pdf_b64)
Not applicable.
All custom field methods raise NotImplementedError as there is no underlying ATS system to manage custom fields.

Implementation Notes

Webhook-Based Architecture

The Public API integration works differently from traditional ATS integrations:
  1. Inbound: Organizations push job and candidate data to Tenzo via Public API
  2. Processing: Tenzo processes interviews and generates results
  3. Outbound: Results are sent back via configured webhooks

Temporary Storage

The client uses temporary storage fields:
  • _temp_candidate: Stores candidate data during processing
  • _temp_resume: Stores resume data during processing

Post-Call Results

Override of post_call_results() method sends interview results via webhook instead of writing back to an ATS.

Job Cache Usage

Jobs created via Public API are stored in AtsJobCache and retrieved from there rather than from an external system.

Webhook Integration

The Public API client integrates with the webhook service (webhook_service.py) to:
  • Send interview results to customer endpoints
  • Notify customers of processing completion
  • Deliver structured interview data

Known Limitations

Not a Traditional ATS
This integration does not connect to an external ATS system. It’s designed for organizations that want to integrate Tenzo via API/webhooks rather than ATS connectivity.
Minimal Implementation
Many methods raise NotImplementedError because they don’t apply to webhook-based integrations. This is expected and intentional.

Use Cases

The Public API integration is appropriate for:
  • Organizations without a traditional ATS
  • Custom-built applicant management systems
  • Integration with internal HR systems
  • Webhook-based workflows

Meeting Copilot Endpoints

The Public API exposes a set of Meeting Copilot resources. The read-only getters — list_meeting_copilot_notes, get_meeting_copilot_note, list_meeting_copilot_templates, and get_meeting_copilot_template — are also surfaced as MCP tools and documented under the Meeting Copilot tag in the Tenzo Public API reference.

Create a Meeting Copilot note

Endpoint: POST /api/v1/meeting-copilot-note Hand a finished transcript to Tenzo and have it build a Meeting Copilot note, optionally running the same post-meeting pipeline the in-app bot uses when a meeting ends. Because this is a write operation, it is not exposed as an MCP tool (unlike the read-only Meeting Copilot getters) — call the REST API directly. The customer-facing contract (request body, transcript limits, response, errors, and a worked example) lives in the Meeting Copilot guide. The generated Tenzo Public API reference covers the same routes under the Meeting Copilot tag. Keep the field tables in the guide only — don’t re-add them here. Internal notes:
  • Access is gated on the MEETING_NOTES_ACCESS flag via require_notes_access; orgs without it get a 403 on every call regardless of key validity.
  • Transcript caps are enforced in CreateMeetingCopilotNoteRequest (5000 lines, 20,000 chars/line, 800,000 total UTF-8 bytes) to keep the note under the Cosmos ~2MB item limit. The byte cap is a Pydantic model_validator, so it is not expressible in the generated JSON Schema — the guide states it in prose instead.
  • If save_transcript fails the just-created note is deleted and the caller gets a 503, so a retry starts clean.
  • The optional per-note config fields (ats_writes — a single flag mapped to both internal toggles update_ats_job and write_candidate_fields_to_ats — plus intake_field_overrides, remote_ats_job_id, description_draft, company_remote_id, contact_remote_id) mirror the note’s in-app Config panel. All but intake_field_overrides are create_note parameters; the overrides are applied via save_note (the same partial-update path the config WebSocket uses) before the transcript is saved and the pipeline scheduled. None/omitted means “inherit the template default”.
  • company_remote_id and contact_remote_id are intake-only. When provided, the controller queries the org’s ATS client (get_job_creation_entity_ref) to resolve human-readable labels (e.g. Bullhorn ClientCorporation and ClientContact) and populates job_creation_params.entity_refs, unlocking the ATS Sync entity pickers.
  • An optional Idempotency-Key HTTP header caches successful responses in Redis for 24 hours (public_api:meeting_note:idempotency:{org_id}:{key}), returning identical cached responses on network retries without creating duplicate notes.
  • A tag only renders in the published ReDoc reference if it appears in both spec["tags"] and x-tagGroups in build_public_openapi(). Adding a route with a brand-new tag without updating both silently omits it from the docs — find_unrendered_route_tags() catches this in a unit test (routes marked include_in_schema=False, like the MCP-only read-only getters, are intentionally excluded).
The public OpenAPI spec is generated at runtime by build_public_openapi() (server/ats/public_api/public_api.py) and served from the live API — it is deliberately not committed to this repo. Do not add a checked-in copy; it would immediately go stale.
  • Implementation: server/ats/public_api/public_api_ats_client.py
  • Webhook Service: server/ats/public_api/webhook_service.py
  • Public API Endpoints: server/ats/public_api/public_api.py
  • Application Controller: server/ats/public_api/public_application_controller.py
  • Job Controller: server/ats/public_api/public_job_controller.py
  • Meeting Copilot Note Controller: server/ats/public_api/public_meeting_note_controller.py
  • README: server/ats/public_api/README.md

See Also