HireNewTalent.ai Public API Documentation
Build end-to-end hiring workflows into your product. Search vetted talent, create engagements, review AI interview scores, send offers, manage lifecycle actions, and message talent through the REST API. The official MCP server exposes the core agent-ready workflow as native tools.
POST /v1/engagements
Authorization: Bearer hnt_test_...
{
"role_title": "Executive VA",
"next_action": "review_score_then_offer_or_pass",
"valid_actions": ["offer", "pass", "live-interview"]
}Quickstart
Make your first request
All endpoints are versioned under /v1. Authenticate with a bearer API key from the client dashboard.
Base URL
https://api.hirenewtalent.ai
curl -X POST https://api.hirenewtalent.ai/v1/search \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"query": "executive assistant with calendar and travel experience"}'Client Dashboard
Create an API key
API keys are created from the client dashboard. Start with a test key while building, then create a live key when your integration is ready for production traffic.
Dashboard path
/dashboard/api-access- Open API Access. Sign in as a client and choose API Access from the Client Dashboard sidebar.
- Choose Test or Live. Test keys use the
hnt_test_prefix and do not require billing. Live keys require a connected billing account. - Name the key. Use a recognizable name such as Local Dev, Staging, or Production.
- Generate and copy once. Select Generate Key, copy the plaintext key from the confirmation dialog, and store it in your backend secret manager or environment variables.
Authentication
Bearer keys and scopes
API keys are scoped to one client account. Every read and write is automatically limited to that client's data.
search
Talent search and profile lookup.
pricing
Role-level pricing estimates.
engagements
Engagement creation and lifecycle actions.
messages
Client/talent conversations over REST.
talent.
Prior interview history and score review.
interviews
Custom interview generation and interview workflows.
Authorization: Bearer hnt_test_YOUR_API_KEYContent-Type: application/jsonSecurity best practices
- Store API keys in a secret manager or backend environment variable.
- Send keys only in the Authorization header.
- Never place keys in URLs, browser bundles, analytics payloads, or logs.
- Rotate keys immediately if a key may have been exposed.
MCP server
Native tools for AI agents
Use the official Model Context Protocol server when your agent runs in Claude Desktop, Cursor, Claude Code, Cline, Codex, or another MCP-capable client. It wraps the full hiring workflow as native tool calls, so agents can search talent, fetch profiles, estimate pricing, review interviews, create engagements, poll the event feed, send offers, message talent, and manage lifecycle actions without writing HTTP plumbing.
Package
@hirenewtalentai/mcp-serverStart with a hnt_test_* key. Test keys exercise the full workflow without sending real notifications or triggering live billing behavior.
Claude Code — one command
claude mcp add hirenewtalent \--env HIRENEWTALENT_API_KEY=hnt_test_YOUR_API_KEY \-- npx -y @hirenewtalentai/mcp-serverClaude Desktop / Cursor / Cline — config
{"mcpServers": {"hirenewtalent": {"command": "npx","args": ["-y", "@hirenewtalentai/mcp-server"],"env": {"HIRENEWTALENT_API_KEY": "hnt_test_YOUR_API_KEY"}}}}Codex — one command
codex mcp add hirenewtalent \--env HIRENEWTALENT_API_KEY=hnt_test_YOUR_API_KEY \-- npx -y @hirenewtalentai/mcp-serverCodex uses TOML, not JSON — the command above writes an [mcp_servers.hirenewtalent] block to ~/.codex/config.toml.
Available MCP tools
These cover the hiring workflow end to end — search, full profile fetches, pricing, interviews, engagements and lifecycle actions, the events feed, and talent messaging. The same command, args, and env block works across most MCP-aware clients.
Responses
Consistent envelopes
Successful responses return a data object and optional metadata. Errors return a stable machine-readable code and a request ID for support.
{"data": {"id": "eng_abc123","status": "pending_decision","next_action": "review_score_then_offer_or_pass","valid_actions": ["offer", "pass", "live-interview"]},"meta": {"request_id": "req_abc123","timestamp": "2026-04-13T12:00:00.000Z","environment": "live"}}{"error": {"code": "invalid_state_transition","message": "Cannot send offer: engagement is in invited state","request_id": "req_abc123","details": {"current_state": "invited","attempted_action": "offer","valid_actions": []}}}Definitions
Data object fields
Common fields returned inside API data objects. Some fields appear only on specific resources or lifecycle states.
| Field | Type | Definition |
|---|---|---|
| id | string | Unique identifier for the returned resource. The resource type depends on the endpoint, such as a talent, engagement, conversation, or message. |
| full_name | string | Talent's display name. |
| title | string | Talent profile title or role label, such as Executive VA or Bookkeeper. |
| match_score | number | Search relevance score from 0 to 1, where higher means a stronger match to the query. |
| bio | string | Short profile summary describing the talent's background and strengths. |
| skills | string[] | List of skills, tools, or capabilities associated with the talent profile. |
| query | string | The search query submitted to the API. |
| total_matched | number | Total number of profiles that matched the search before result limiting or curation. |
| string | Email address associated with a profile when available to the authenticated client. | |
| avatar_url | string | URL for the talent profile image. |
| location | string | Talent's country, city, or general location. |
| talent_profiles | object | Nested talent profile details including title, skills, experience, availability, rating, and review count. |
| talent_experiences | object[] | Work history entries for a talent profile. |
| talent_profile_id | string | Identifier of the talent profile used to create an engagement. |
| engagement_id | string | Unique identifier for an engagement workflow. |
| invite_id | string | Unique identifier for the AI interview invite connected to an engagement. |
| status | string | Current lifecycle state of an engagement, such as invited, pending_decision, offer_sent, active, passed, terminated, or completed. |
| environment | string | Environment associated with the response. Public production workflows return live. |
| interview_mode | string | Engagement creation mode: request_new sends a fresh interview; use_prior_score decides on an existing prior score when available. |
| prior_invite_id | string | Optional prior interview invite identifier to use when interview_mode is use_prior_score. |
| score | object | AI interview score data, including overall score, rubric results, strengths, improvements, and summary when available. |
| overall_score | number | Aggregate interview score used to summarize candidate fit. |
| rubric_results | object[] | Detailed scoring results for each interview rubric category. |
| strengths | string[] | Positive signals identified from the interview. |
| improvements | string[] | Areas where the candidate may need support, development, or additional screening. |
| summary | string | Narrative summary of the interview result or candidate fit. |
| english_fluency | string | English fluency classification returned with interview results when available. |
| next_action | string | Machine-readable hint telling your integration what to do next. |
| valid_actions | string[] | List of lifecycle actions currently accepted for the engagement. |
| invite_status | string | Current status of the interview invite, such as pending or completed. |
| own_interview | object | The interview commissioned by this engagement, including detailed score data once completed. |
| prior_interviews | object[] | Sanitized cross-company prior interview history for the talent, subject to opt-out and privacy stripping. |
| engagements_billing | object | Billing cadence and hours associated with an engagement. |
| engagement_pricing | object | Client-facing locked price for an engagement after the interview invite is sent: client_rate, client_period_cost, billing_type, and hours_per_period. |
| role_title | string | Role being hired for. For AI interview workflows, this should map to a supported role template. |
| talent_wage_rate | number | Engine-controlled talent-side hourly rate returned in pricing quotes. Ignored if supplied by callers. |
| client_rate | number | Engine-controlled client hourly rate returned in pricing quotes, engagement_pricing, and rate-lock events. Ignored on offer requests. |
| hours_per_period | number | Integer number of hours in the billing period. Valid values depend on billing_type. |
| billing_type | string | Billing cadence for pricing and engagements. Supported values are monthly_recurring, rolling_4_week, and rolling_biweekly. |
| country_region | string | Pricing region hint: philippines, india, latin_america, or default. |
| start_date | string | ISO date for the expected engagement start date. |
| job_description | string | Description of the work, responsibilities, or outcomes expected for the engagement. |
| replacement_requested | boolean | Whether a replacement request was submitted for an active engagement. |
| message | string | Human-readable confirmation or status message. |
| conversation_id | string | Unique identifier for a client-to-talent conversation. |
| messages | object[] | Paginated list of messages in a conversation. |
| participant_1_id | string | Identifier for one participant in a conversation. |
| participant_2_id | string | Identifier for the other participant in a conversation. |
| sender_id | string | Identifier of the user or system that sent a message. |
| sender_type | string | Classification of the sender, such as human. |
| content | string | Message body text. |
| is_read | boolean | Whether the message has been marked as read. |
| last_message_at | string | Timestamp of the most recent message in a conversation. |
| last_message_preview | string | Short preview of the latest message in a conversation. |
| events | object[] | Paginated engagement event records from the pull-based events feed. |
| next_cursor | string | Opaque events-feed cursor to pass as since on the next poll. |
| has_more | boolean | Whether more event pages may be available after the current response. |
| created_at | string | ISO timestamp for when the resource was created. |
| updated_at | string | ISO timestamp for when the resource was last updated. |
| limit | number | Maximum number of records returned in a paginated response. |
| offset | number | Number of records skipped before returning paginated results. |
Reference
Endpoint overview
Each endpoint requires the listed permission scope. The API returns 403 forbidden when a key lacks the required scope.
Health
Unauthenticated utility endpoint for uptime checks.
/health
Returns status ok and a timestamp. This endpoint is not wrapped in the standard data envelope.
Example query
curl https://api.hirenewtalent.ai/healthExample output
{"status": "ok","timestamp": "2026-04-13T12:00:00.000Z"}Search
Use these endpoints to find talent and retrieve profile details.
/v1/search
Natural-language talent search. Attempts to include estimated_monthly_cost when pricing is available.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/search \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"query": "executive assistant with calendar and travel experience","hours_per_period": 80,"billing_type": "monthly_recurring"}'Example output
{"data": {"query": "executive assistant with calendar and travel experience","total_matched": 24,"results": [{"id": "tal_123","full_name": "Maya Santos","title": "Executive Virtual Assistant","match_score": 0.94,"estimated_monthly_cost": 1040}]}}/v1/talent/:id
Full talent profile, including skills, availability, and work history.
Example query
curl https://api.hirenewtalent.ai/v1/talent/tal_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"id": "tal_123","full_name": "Maya Santos","title": "Executive Virtual Assistant","skills": ["Calendar management", "Travel planning", "Inbox triage"],"location": "Philippines","availability": "full_time"}}Interview history
Check whether a talent has already been interviewed for a role before sending a new invite. Responses are privacy-stripped and always return an empty array (never 404) when the talent opts out or does not exist.
/v1/talent/:id/interviews
Privacy-stripped interview history for a talent. Supports role_title, since, and limit query params. Empty histories return uniformly to protect talent privacy.
Example query
curl "https://api.hirenewtalent.ai/v1/talent/tal_123/interviews?role_title=Executive+Assistant&limit=5" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"interviews": [{"invite_id": "inv_prior_123","role_title": "Executive Assistant","completed_at": "2026-04-10T15:30:00.000Z","overall_score": 88,"summary": "Strong calendar ownership and clear client communication."}]}}Pricing
Quote expected costs before committing to an engagement. The pricing engine is the only source of truth — callers cannot override rates.
/v1/pricing/estimate
Role-level price range (entry_standard to senior_expert) without requiring a specific talent.
Example query
curl "https://api.hirenewtalent.ai/v1/pricing/estimate?role_title=Bookkeeper&hours_per_period=80&billing_type=monthly_recurring" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"role_family": "bookkeeping_accounting","billing_type": "monthly_recurring","hours_per_period": 80,"entry_standard": {"client_rate": 8.25,"client_period_cost": 660},"senior_expert": {"client_rate": 13.5,"client_period_cost": 1080}}}Interview generation
Generate custom interview prompts and rubrics for specialized role titles before creating an engagement. Standard platform roles can go straight to POST /v1/engagements; custom roles should call this endpoint first with persist=true, then create the engagement with the same role_title. Use persist=false only to preview/test the output without saving it.
When to call this: skip this step for standard platform roles and create the engagement directly. For a custom or specialized role, call this endpoint first with persist: true, then create the engagement with the same role_title. The invite flow will resolve your saved client-scoped prompt and rubric automatically.
/v1/interviews/role-config
Generate an AI interview prompt and rubric for a custom role title before engagement creation. persist=false previews the output without saving; persist=true saves it for later engagements.
Example query
curl -X POST "https://api.hirenewtalent.ai/v1/interviews/role-config" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"role_title": "Shopify customer support lead","persist": true}'Example output
{"data": {"role_title": "Shopify customer support lead","prompt": "Assess Shopify support judgment, escalation habits, and customer empathy.","rubric": [{"name": "Platform fluency","weight": 30,"description": "Understands Shopify orders, refunds, subscriptions, apps, and common store admin workflows."},{"name": "Customer communication","weight": 25,"description": "Writes clear, empathetic responses and adapts tone across email, chat, and escalations."},{"name": "Troubleshooting judgment","weight": 20,"description": "Diagnoses customer issues, asks useful follow-up questions, and separates urgent bugs from routine requests."},{"name": "Escalation and ownership","weight": 15,"description": "Knows when to involve operations, engineering, or management while keeping the customer informed."},{"name": "Process improvement","weight": 10,"description": "Identifies repeat issues and suggests macros, help-center updates, or workflow improvements."}],"persisted": {"prompt_template_id": "tpl_prompt_123","rubric_template_id": "tpl_rubric_123","role_mapping_id": "map_123"}}}Engagements
Manage the hiring lifecycle from interview invite through offer, active engagement, replacement, or closeout.
/v1/engagements
Create an engagement. For custom role interviews, first call /v1/interviews/role-config with persist=true, then reuse the same role_title here. request_new sends a fresh AI interview; use_prior_score skips to pending_decision when a prior score exists.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"talent_profile_id": "tal_123","role_title": "Executive Assistant","interview_mode": "request_new","hours_per_period": 80,"billing_type": "monthly_recurring"}'Example output
{"data": {"id": "eng_123","talent_profile_id": "tal_123","status": "invited","interview_mode": "request_new","next_action": "wait_for_interview_completion","valid_actions": []}}/v1/engagements
List all engagements for the authenticated client.
Example query
curl "https://api.hirenewtalent.ai/v1/engagements?limit=20&offset=0" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"engagements": [{"id": "eng_123","status": "pending_decision","role_title": "Executive Assistant","next_action": "review_score_then_offer_or_pass","valid_actions": ["offer", "pass", "live-interview"]}],"limit": 20,"offset": 0}}/v1/engagements/:id
Retrieve one engagement with score, billing, invite, next_action, and valid_actions.
Example query
curl https://api.hirenewtalent.ai/v1/engagements/eng_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"id": "eng_123","status": "pending_decision","role_title": "Executive Assistant","next_action": "review_score_then_offer_or_pass","valid_actions": ["offer", "pass", "live-interview"],"own_interview": {"overall_score": 88,"summary": "Strong fit for executive support."},"engagement_pricing": {"client_rate": 13,"hours_per_period": 80,"billing_type": "monthly_recurring"}}}/v1/engagements/:id
Update operational metadata such as start_date. Role title and job description are fixed after creation.
Example query
curl -X PATCH https://api.hirenewtalent.ai/v1/engagements/eng_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"start_date": "2026-05-01"}'Example output
{"data": {"id": "eng_123","start_date": "2026-05-01","updated_at": "2026-04-13T12:00:00.000Z"}}/v1/engagements/:id/offer
Send an offer using the engine-stamped rate from invite time. No body required; rate fields are ignored.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/offer \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"id": "eng_123","status": "offer_sent","next_action": "wait_for_talent_response","valid_actions": [],"message": "Offer sent."}}/v1/engagements/:id/pass
Pass on a candidate from the decision stage.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/pass \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"id": "eng_123","status": "passed","next_action": "engagement_closed","valid_actions": [],"message": "Candidate passed."}}/v1/engagements/:id/live-interview
Request a live interview before making a final decision.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/live-interview \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"id": "eng_123","status": "live_interview_requested","next_action": "wait_for_live_interview","valid_actions": [],"message": "Live interview requested."}}/v1/engagements/:id/replacement
Request a replacement for an active engagement.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/replacement \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"reason": "Timezone mismatch"}'Example output
{"data": {"id": "eng_123","status": "active","replacement_requested": true,"message": "Replacement requested."}}/v1/engagements/:id/terminate
Terminate an active engagement. Requires a reason.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/terminate \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"reason": "Role no longer needed"}'Example output
{"data": {"id": "eng_123","status": "terminated","next_action": "engagement_ended","valid_actions": [],"message": "Engagement terminated."}}Messaging
Embed client-to-talent communication inside your own application.
/v1/conversations
List conversations for the authenticated client.
Example query
curl "https://api.hirenewtalent.ai/v1/conversations?limit=20&offset=0" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"conversations": [{"id": "conv_123","participant_2_id": "tal_123","last_message_preview": "Thanks, I can start Monday.","last_message_at": "2026-04-13T12:00:00.000Z"}],"limit": 20,"offset": 0}}/v1/conversations
Get or create a conversation with a talent profile.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/conversations \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"talent_profile_id": "tal_123"}'Example output
{"data": {"id": "conv_123","participant_1_id": "client_123","participant_2_id": "tal_123","created_at": "2026-04-13T12:00:00.000Z"}}/v1/conversations/:id/messages
Fetch paginated messages in a conversation.
Example query
curl "https://api.hirenewtalent.ai/v1/conversations/conv_123/messages?limit=25&offset=0" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"messages": [{"id": "msg_123","conversation_id": "conv_123","sender_type": "human","content": "Can you start next Monday?","is_read": true,"created_at": "2026-04-13T12:00:00.000Z"}],"limit": 25,"offset": 0}}/v1/conversations/:id/messages
Send a message to a conversation.
Example query
curl -X POST https://api.hirenewtalent.ai/v1/conversations/conv_123/messages \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"content": "Can you start next Monday?"}'Example output
{"data": {"id": "msg_123","conversation_id": "conv_123","sender_type": "human","content": "Can you start next Monday?","created_at": "2026-04-13T12:00:00.000Z"}}Events
Catch up on asynchronous engagement changes with one pull-based feed across the authenticated client's engagements.
/v1/events
Paginated event feed with opaque since cursor, limit clamped to 1-100, and at-least-once delivery semantics.
Example query
curl "https://api.hirenewtalent.ai/v1/events?limit=50&since=opaque_cursor_123" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"events": [{"id": "evt_123","event_type": "engagement.status_changed","payload": {"engagement_id": "eng_123","previous_status": "invited","new_status": "pending_decision","next_action": "review_score_then_offer_or_pass"},"environment": "test","schema_version": 1,"created_at": "2026-04-13T12:00:00.000Z"}],"next_cursor": "opaque_cursor_456","has_more": false}}Request fields at a glance
These are the fields most integrations send directly. Response-only fields and lifecycle-specific objects are covered in the data definitions above.
Search body
| Field | Type | Notes |
|---|---|---|
| query | string | Required. Natural-language role, skills, and experience description. Maximum 500 characters. |
| deviceId | string | Optional. Client device identifier used by the search flow. |
| hours_per_period | number | Optional. Billing hours used when returning estimated_monthly_cost. Defaults to 80, or 40 for rolling_biweekly. |
| billing_type | string | Optional. monthly_recurring, rolling_4_week, or rolling_biweekly. Defaults to monthly_recurring. |
| country_region | string | Optional pricing context: philippines, india, latin_america, or default. |
Pricing estimate query
| Field | Type | Notes |
|---|---|---|
| role_title | string | Required. Free-text role title. Maximum 120 characters. Unknown titles fall back to a general VA estimate. |
| hours_per_period | number | Required. 80 or 160 for monthly/4-week billing; 40 or 80 for biweekly billing. |
| billing_type | string | Required. monthly_recurring, rolling_4_week, or rolling_biweekly. |
| country_region | string | Optional. philippines, india, latin_america, or default. |
Create engagement body
| Field | Type | Notes |
|---|---|---|
| talent_profile_id | string | Required. The talent_id returned by search. |
| role_title | string | Required. Role being hired for. Maximum 120 characters. |
| job_description | string | Optional. Job details used by pricing complexity classification and interview context. Maximum 5,000 characters. |
| hours_per_period | number | Optional. Billing hours for the engine-stamped quote. |
| billing_type | string | Optional. Billing cadence for the engagement. |
| interview_mode | string | Optional. request_new sends a fresh interview; use_prior_score decides on a prior score when available. |
| prior_invite_id | string | Optional with use_prior_score. Selects a specific completed prior interview. |
Lifecycle action fields
| Field | Type | Notes |
|---|---|---|
| reason | string | Required for terminate; optional for replacement. Maximum 2,000 characters when provided. |
| start_date | ISO date | Optional PATCH field for expected engagement start date. |
Interview history query
| Field | Type | Notes |
|---|---|---|
| role_title | string | Optional. Exact, case-insensitive role-title filter. |
| since | ISO datetime | Optional. Return only interviews completed after this timestamp. |
| limit | number | Optional. Defaults to 20; maximum 100. |
Interview generation body
| Field | Type | Notes |
|---|---|---|
| role_title | string | Required. Free-text role title for the generated interview. |
| seniority | string | Optional. Free-text seniority hint. |
| responsibilities | string | Optional. Responsibilities the interview should evaluate. |
| must_have_skills | string[] | Optional. Skills or requirements that should appear in the rubric. |
| interview_goals | string | Optional. Assessment priorities for the generated interview. |
| persist | boolean | Optional. Defaults to false. false previews/tests the output without saving; true saves the generated role configuration for later engagements. |
Messaging fields
| Field | Type | Notes |
|---|---|---|
| content | string | Required. Message body. Empty content is rejected. Maximum 5,000 characters. |
| limit | number | Optional for message list requests. Defaults to 50. Valid range: 1-100. |
| offset | number | Optional for message list requests. Defaults to 0. Valid range: 0-10,000. |
Pricing guide
Quote before you commit
The role-level estimate endpoint lets you preview costs before picking a talent. The exact talent-specific offer rate is engine-controlled and locked at invite time; callers cannot override it in request bodies. Pricing reflects your submitted hours and billing type exactly.
After POST /v1/engagements sends the interview invite, query GET /v1/engagements/:id and read engagement_pricing.client_rate for the locked client-facing rate.
Role range estimate
Returns entry_standard and senior_expert quotes for any role title — no specific talent required. Unknown titles fall back to general_va. The role_family field in the response shows which band was resolved.
curl "$HNT_URL/v1/pricing/estimate?\role_title=Bookkeeper+for+SaaS+startup&\hours_per_period=80&\billing_type=monthly_recurring" \-H "Authorization: Bearer $HNT_KEY"{"data": {"role_family": "bookkeeping_accounting","country": "philippines","billing_type": "monthly_recurring","hours_per_period": 80,"entry_standard": {"talent_wage_rate": 5.50,"client_rate": 8.25,"client_period_cost": 660.00},"senior_expert": {"talent_wage_rate": 9.00,"client_rate": 13.50,"client_period_cost": 1080.00}}}Pricing is engine-controlled
The API does not accept caller-supplied rates or caller-supplied billing terms on rate-locked operations. These fields are silently ignored regardless of what you send:
talent_wage_rateonPOST /v1/engagements— the engine computes the talent rate at invite time. Response carriesX-Ignored-Fields: talent_wage_rate.client_rate,hours_per_period, andbilling_typeonPOST /v1/engagements/:id/offer— the API uses the saved engine-stamped quote and billing cadence from invite time. Response carriesX-Ignored-Fields: client_rate, hours_per_period, billing_type.
Use GET /v1/pricing/estimate for a pre-commitment role-level range. POST /v1/engagements locks the engine-controlled offer rate for the engagement flow, but caller-supplied rate fields are never used.
Search examples
Find and inspect talent
Search accepts plain-language role descriptions. The optional pricing decoration may be omitted when pricing is unavailable.
curl -X POST https://api.hirenewtalent.ai/v1/search \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"query": "bookkeeper with QuickBooks Online and accounts receivable experience"}'{"data": {"top": [{"talent_id": "talent_123","full_name": "Jane Doe","title": "Bookkeeper","match_score": 0.92,"bio": "Experienced in QuickBooks cleanup and monthly close.","skills": ["QuickBooks", "Accounts Receivable", "Reconciliation"]}],"query": "bookkeeper with QuickBooks Online and accounts receivable experience","total_matched": 42}}curl https://api.hirenewtalent.ai/v1/talent/talent_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"{"data": {"id": "talent_123","full_name": "Jane Doe","avatar_url": "https://example.com/avatar.jpg","location": "Philippines","talent_profiles": {"title": "Bookkeeper","skills": ["QuickBooks", "Xero", "Payroll"],"experience_years": 6,"availability": "available","rating": 4.8},"talent_experiences": [{"title": "Accounting Assistant","company": "Remote Services Co.","description": "Handled reconciliations, AR, AP, and reporting."}]}}Engagement examples
Start and manage hiring workflows
Create an engagement to start the interview workflow. Then poll the engagement until it reaches the decision stage and follow the returned valid actions.
curl -X POST https://api.hirenewtalent.ai/v1/engagements \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"talent_profile_id": "talent_123","role_title": "Bookkeeper / Accountant","job_description": "Manage QuickBooks, reconciliations, AR follow-up, and monthly reporting.","hours_per_period": 80,"billing_type": "monthly_recurring"}'{"data": {"engagement_id": "eng_123","invite_id": "invite_123","status": "invited","environment": "test","interview_mode": "request_new","own_interview": {"invite_id": "invite_123","status": "pending","score": null,"english_fluency": null},"prior_interviews": [],"next_action": "wait_for_interview_completion","valid_actions": []}}curl https://api.hirenewtalent.ai/v1/engagements/eng_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"{"data": {"engagement_id": "eng_123","status": "pending_decision","invite_status": "completed","engagement_pricing": {"client_rate": 8.25,"client_period_cost": 660.00,"billing_type": "monthly_recurring","hours_per_period": 80},"score": {"overall_score": 87,"strengths": ["Clear communication", "Strong bookkeeping experience"],"improvements": ["Would benefit from more ERP exposure"],"summary": "Strong fit for bookkeeping and AR support."},"english_fluency": "fluent","next_action": "review_score_then_offer_or_pass","valid_actions": ["offer", "pass", "live-interview"]}}Send an offer
Most integrations send an offer from pending_decision. If a client requests a live interview first, HireNewTalent may later mark the engagement hired after the internal live-interview outcome is recorded. From either state, call POST /v1/engagements/:id/offer. Send an empty object; the API uses the saved engine-stamped quote and billing cadence from invite time.
- Live keys trigger the live offer flow and move the engagement to
offer_sent. - Test keys update status only and return
test_mode: true. - Do not send rates.
client_rate,hours_per_period, andbilling_typeare ignored if present.
# Send an empty body; engine-stamped rate from invite time is used automatically.curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/offer \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{}'curl -X PATCH https://api.hirenewtalent.ai/v1/engagements/eng_123 \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"start_date": "2026-05-01"}'Messaging examples
Create conversations and send messages
Conversations are scoped to the authenticated client. The create endpoint is get-or-create: if a conversation already exists, the response returns its conversation_id.
curl -X POST https://api.hirenewtalent.ai/v1/conversations \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"talent_id": "talent_123"}'curl -X POST https://api.hirenewtalent.ai/v1/conversations/convo_123/messages \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"content": "Hi Jane, we reviewed your interview and would like to discuss the role."}'curl "https://api.hirenewtalent.ai/v1/conversations/convo_123/messages?limit=25&offset=0" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"{"data": {"messages": [{"id": "msg_123","sender_id": "client_123","sender_type": "human","content": "Hi Jane, we reviewed your interview and would like to discuss the role.","is_read": false,"created_at": "2026-04-13T12:00:00.000Z"}],"limit": 25,"offset": 0}}Message validation
Sending a message requires non-empty content. Messages that fail content moderation return 400 content_moderation.
Lifecycle
Use next_action and valid_actions
Engagement responses tell your integration what to do next. Use these fields instead of hardcoding assumptions from status names.
| Status | next_action | valid_actions |
|---|---|---|
| invited | wait_for_interview_completion | [] |
| pending_decision | review_score_then_offer_or_pass | ["offer", "pass", "live-interview"] |
| live_interview_requested | wait_for_live_interview | [] |
| hired | wait_for_offer_preparation | ["offer"] |
| offer_sent | wait_for_talent_response | [] |
| active | engagement_active | ["replacement", "terminate"] |
| passed | engagement_closed | [] |
| terminated or completed | engagement_ended | [] |
| From | To | Endpoint |
|---|---|---|
| pending_decision | offer_sent | POST /v1/engagements/:id/offer |
| pending_decision | passed | POST /v1/engagements/:id/pass |
| pending_decision | live_interview_requested | POST /v1/engagements/:id/live-interview |
| live_interview_requested | hired | Internal/admin live interview outcome |
| live_interview_requested | passed | Internal/admin live interview outcome |
| hired | offer_sent | POST /v1/engagements/:id/offer |
| active | terminated | POST /v1/engagements/:id/terminate |
Retry-safe transitions
The offer, pass, live-interview, and terminate transition endpoints return 200 when the engagement is already in the target state. Those retry responses include meta.idempotent: true, for example calling pass again after the engagement is already passed. POST /v1/engagements/:id/replacement is not a status transition and can create a new replacement request each time.
Polling guidance
- Use
GET /v1/engagements/:idfor one active workflow andGET /v1/eventswhen tracking multiple workflows. - Store the latest
next_cursorfrom the events feed and pass it back assinceon the next request. - Back off when no changes are returned, and always rely on
next_actionplusvalid_actionsbefore showing user actions.
curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/pass \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/live-interview \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"curl -X POST https://api.hirenewtalent.ai/v1/engagements/eng_123/replacement \-H "Authorization: Bearer hnt_test_YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"reason": "Timezone mismatch"}'Events feed
Poll /v1/events to catch up on asynchronous changes across all engagements. Delivery is at least once, so dedupe by event id and ignore event types your integration does not recognize.
Example query
curl "https://api.hirenewtalent.ai/v1/events?limit=50" \-H "Authorization: Bearer hnt_test_YOUR_API_KEY"Example output
{"data": {"events": [{"id": "evt_123","event_type": "engagement.status_changed","payload": {"engagement_id": "eng_123","previous_status": "invited","new_status": "pending_decision","next_action": "review_score_then_offer_or_pass"},"environment": "test","schema_version": 1,"created_at": "2026-04-13T12:00:00.000Z"}],"next_cursor": "opaque_cursor_123","has_more": false}}| Event type | When it fires | Common fields |
|---|---|---|
| engagement.created | A new engagement is created. | payload.engagement_id, payload.interview_mode |
| engagement.rate_locked | The engine-stamped rate is locked for an offer. | payload.engagement_id, payload.client_rate, payload.hours_per_period, payload.billing_type |
| engagement.status_changed | The engagement changes lifecycle state. | payload.engagement_id, payload.previous_status, payload.new_status, payload.next_action |
| engagement.replacement_requested | A replacement is requested on an active engagement. | payload.engagement_id, payload.reason |
Test mode
Exercise workflows before going live
API keys beginning with hnt_test_ let you validate the integration without live billing or notification behavior. Test-only endpoints require test keys and test engagements.
| Area | Test behavior |
|---|---|
| Search and profile reads | Return real marketplace data. |
| Engagement creation | Creates a test engagement and does not contact talent. |
| Offer and talent response | State changes can be exercised without live billing or notifications. |
| Messages | Messages are stored for testing without live notification behavior. |
| Test endpoint | Use |
|---|---|
| POST /v1/engagements/:id/_test/complete-interview | Simulates interview completion and moves invited to pending_decision. |
| POST /v1/engagements/:id/_test/accept-offer | Simulates offer acceptance and moves offer_sent to active. |
| POST /v1/engagements/:id/_test/decline-offer | Simulates offer decline and moves offer_sent to passed. |
| Error | Meaning |
|---|---|
| 403 forbidden | A live key is used, or the engagement is not a test engagement. |
| 400 invalid_state | The engagement is not in the state required for the simulated action. |
| 400 not_applicable | The simulated action does not apply to the engagement mode, such as completing an interview for use_prior_score. |
| 404 not_found | The engagement does not exist or is not owned by the authenticated client. |
Rate limits
Plan for retries and backoff
Authenticated rate limits are applied per client account. Paid limits scale with active hires, and responses include standard rate-limit headers so your client can throttle before hitting a hard limit.
| Tier | Requests/min | Requests/day |
|---|---|---|
| Free | 60 | 1,000 |
| Paid | 300 + 30 per hire, capped at 1,500 | 10,000 + 1,000 per hire, capped at 100,000 |
| Enterprise | 600 or custom | 50,000 or custom |
RateLimit-Limit: 60RateLimit-Remaining: 58RateLimit-Reset: 1744545600 HTTP/1.1 429 Too Many RequestsRetry-After: 42Errors
Handle errors by code
Switch on the stable error code instead of parsing human-readable messages. Include request_id when contacting support.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | validation_error | Missing or invalid request field. |
| 400 | invalid_action | The requested action name is unknown. |
| 400 | invalid_prior_invite | The requested prior interview cannot be used for this talent. |
| 400 | invalid_state_transition | The requested action is not valid for the engagement's current state. |
| 400 | invalid_state | The resource exists, but the requested operation cannot be performed yet. |
| 400 | no_prior_interview | use_prior_score was requested, but no completed prior interview is available. |
| 400 | not_applicable | The requested test-mode action does not apply to this engagement mode. |
| 400 | role_not_supported | The role_title does not map to a supported interview template. |
| 400 | content_moderation | Message content was blocked. |
| 401 | unauthorized | Missing, invalid, expired, or revoked API key. |
| 403 | forbidden | The API key does not have the required permission scope. |
| 404 | not_found | The resource does not exist or is not owned by the authenticated client. |
| 409 | no_stamped_quote | The engagement does not have the engine-stamped quote required to send an offer. |
| 429 | rate_limited | The client account exceeded its per-minute or per-day request limit. |
| 500 | database_error | An underlying data operation failed. |
| 4xx/500 | engagement_creation_failed | The engagement could not be created. |
| 4xx/500 | generation_failed | Custom interview generation failed. |
| 500 | persist_failed | Custom interview generation succeeded, but persistence failed. |
| 4xx/500 | offer_failed | The offer could not be sent. |
| 4xx/500 | pricing_failed | The pricing estimate could not be generated. |
| 4xx/500 | search_failed | Search could not be completed. |
| 500 | internal_error | An unexpected API error occurred. |
| 503 | service_unavailable | A required API service is unavailable. |
const response = await fetch(url, {headers: { Authorization: `Bearer ${process.env.HNT_API_KEY}` },}); const body = await response.json(); if (!response.ok) {const { code, message, details } = body.error; if (code === "rate_limited") {const retryAfter = Number(response.headers.get("Retry-After") || "60");await sleep(retryAfter * 1000);return retryRequest();} if (code === "invalid_state_transition") {return handleValidActions(details.valid_actions);} throw new Error(`${code}: ${message}`);}Ready to build with the API?
Create an API key from your client dashboard, keep it in your backend, and start with search plus engagement polling.