Contents
v1Partner API
Bypass Monitor API Integration
Know when introductions get hired
Submit candidates you introduced to clients and detect when they appear at those companies, with evidence and timing analysis.
Best fit for
Staffing and recruitment agencies monitoring client submissions
RPO providers verifying introduction outcomes
ATS vendors adding bypass detection to agency workflows
Teams that submit candidates to multiple clients and need hire visibility
Partners who already track submission dates and client company names
Integration flow
Get approved and receive an API key
Request access from TieTalent. Once approved, you receive an X-API-Key for your integration.
Submit a bypass check
POST to /api/v1/bypass-checks with candidate identity anchors, client submissions, and declared experience (or profile_urls if you have no experience to send).
Receive 202 Accepted
The response includes a check id, status: pending, candidate_id, and additive credits / plan. There is no top-level cached boolean (unlike Talent Update); credits.cached is present and false because bypass checks are not reused. Always poll or wait for the webhook.
Poll or wait for a webhook
GET /api/v1/bypass-checks/{id} until status is completed or failed. Webhooks (bypass_check_ready / bypass_check_failed) are preferred when configured.
Act on bypass evidence
When bypass.detected is true, review bypass.matches for submitted company, found employer, match quality, and hire timing relative to your submission date.
Authentication
X-API-Key header on every request, identical to Candidate Intelligence and Talent Update. A request may carry at most 50 candidates, and each candidate spends one unit of your per-minute rate limit.Request headers
X-API-Key: ats_your_api_key_here Content-Type: application/json
Create bypass check
Creates an asynchronous bypass check. TieTalent runs a talent update internally, then compares discovered employers against your submission list.
Send complete submissions with submission dates whenever you have them; timing analysis depends on submitted_at.
{
"candidate": {
"id": "cand-9911",
"first_name": "Alex",
"last_name": "Rivera",
"email": "alex.rivera@example-corp.com",
"employer_website": "example-corp.com",
"role": "Software Engineer",
"location": "London, UK",
"profile_urls": [
"https://linkedin.com/in/alex-rivera-example"
],
"experience": [
{
"company": "Previous Corp",
"role": "Engineer",
"start_date": "2014-01",
"end_date": "2016-01",
"is_current": false
}
],
"submissions": [
{ "company": "Acme Client Ltd", "submitted_at": "2025-03" },
{ "company": "Beta Industries AG", "submitted_at": "2024-11" }
]
}
}Request fields
candidate (required object)
Includes all Talent Update identity fields plus submissions, and experience unless you supply profile_urls. See also the Talent Update request fields.
Identity combination (at least one)
Fields
Accuracy
A
first_name + last_name + profile_urls
Strongest and cheapest. A profile URL removes any doubt about who we are researching.
B
first_name + last_name + (employer_website or company)
Strong. The employer is what confirms the person is yours and not a namesake.
C
email (corporate)
Good. A corporate domain gives us the employer and the name is read from the address, so it works on its own. Send a name too where you have one.
D
email (personal) + (employer_website or company)
Good when the address carries the name, for example jane.doe@gmail.com. A personal domain says nothing about the employer, so the company is what anchors the search and is required. Send first_name and last_name too where you have them: an address such as jdoe92@ gives us little to work with, and the profile can come back empty.
E
employer_website + role
Weakest, and only for candidates whose name you do not hold. It identifies a post rather than a person, so we return a result only when exactly one person holds that role, never above medium confidence.
idYour identifier for the candidate. Echoed as candidate_id.first_name, last_name, email, employer_website, company, role, location, profile_urlsSame rules as Talent Update.experienceOptional when profile_urls is supplied, otherwise required (max 40). Employment you already know about, used to distinguish new hires from previously known employers. Send an empty array when you have none and give us a profile URL instead.submissionsRequired. At least one entry (max 100). Each submission: company (client you submitted to) and optional submitted_at (YYYY-MM or YYYY-MM-DD).submissions[] object
companyClient company name as you recorded the submission. Required.submitted_atWhen you submitted the candidate to this client. Used for timing analysis.Optional top-level fields
mode"precompute" for background batch checks. See Precompute checks.Not accepted
No CV or file upload. Identity anchors and structured submission metadata only.
Field rules
Validation failures return 422 with application/problem+json.
experienceRequired unless you send profile_urls. Each entry requires company. Include start_date and end_date where you have them: they let us tell a past stint at a client from a new one.submissionsMinimum one entry. Each requires company. Maximum 100 per candidate.submitted_atYYYY-MM or YYYY-MM-DD when provided.emailCorporate or personal (same rules as Talent Update). A personal address needs company or employer_website with it.Optional fieldsMay be omitted or sent as null. Both are treated as absent, so "end_date": null on a current role is accepted.Precompute checks
Use POST /api/v1/bypass-checks with mode: "precompute" to warm checks in the background before a recruiter opens a submission record.
candidates array. Live calls require a single candidate object. There is no force_refresh or create-time cache flag on bypass checks.Batch precompute
Send mode: "precompute" with candidates instead of candidate. Each candidate must include submissions, and either experience or profile_urls.
{
"mode": "precompute",
"candidates": [
{
"id": "cand-9911",
"first_name": "Alex",
"last_name": "Rivera",
"email": "alex.rivera@example-corp.com",
"experience": [
{ "company": "Previous Corp", "is_current": false }
],
"submissions": [
{ "company": "Acme Client Ltd", "submitted_at": "2025-03" }
]
}
]
}Usage volume is metered by your plan credits; how fast you may spend them is metered by your plan's requests-per-minute limit, which live calls, precompute calls and CSV enrichment all share. Exceeding the rate limit returns 429 Too Many Requests with error rate-limit-exceeded and a Retry-After; retry after that delay. See Rate limits for the per-plan limits and response headers.
Create response
A successful create returns 202 Accepted with status: pending, plus additive credits (remaining balance and charge for this call) and plan (current plan) objects. These fields are non-breaking and do not require a version bump. Batch precompute returns an items array and top-level credits / plan. There is no top-level cached boolean (unlike Talent Update); credits.cached is present and always false because bypass checks are not reused.
Single-candidate response
{
"id": "bc_01JCXY",
"status": "pending",
"candidate_id": "cand-9911",
"created_at": "2026-08-28T12:56:44.642Z",
"credits": {
"balance": 140,
"plan_credits": 118,
"rollover_credits": 22,
"charged": 2,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
}Batch precompute response
Each item includes id, status: pending, candidate_id, created_at, plus per-item credits and plan.
{
"items": [
{
"id": "bc_01JCXY",
"status": "pending",
"candidate_id": "cand-9911",
"created_at": "2026-08-28T12:56:44.642Z",
"credits": {
"balance": 138,
"plan_credits": 116,
"rollover_credits": 22,
"charged": 2,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
},
{
"id": "bc_01JCXZ",
"status": "pending",
"candidate_id": "cand-9912",
"created_at": "2026-08-28T12:56:45.102Z",
"credits": {
"balance": 138,
"plan_credits": 116,
"rollover_credits": 22,
"charged": 1,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
}
],
"credits": {
"balance": 138,
"plan_credits": 116,
"rollover_credits": 22,
"charged": 3,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
}credits.balance is remaining credits; plan_credits and rollover_credits are the balance buckets; charged is credits reserved or charged for this check (often scaled by unique submission companies), or null when not applicable on GET; credits.cached is always false on bypass checks. plan includes code (free, starter, growth, or custom), cycle (monthly, annual, or null), credits_per_cycle, and period_ends_at. Ignore these fields if unused.
Get bypass check
Call GET /api/v1/bypass-checks/{id} to retrieve the check result.
status values
pendingCheck in progress (includes internal talent update).completedCheck finished. bypass and optional talent_update are populated.failedCheck failed. See error.While status is pending
{
"id": "bc_01JCXY",
"status": "pending",
"candidate_id": "cand-9911",
"created_at": "2026-08-28T12:56:44.642Z",
"completed_at": null,
"bypass": {
"detected": false,
"note": null,
"matches": []
},
"talent_update": null,
"error": null,
"credits": {
"balance": 140,
"plan_credits": 118,
"rollover_credits": 22,
"charged": 2,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
}When status is completed
{
"id": "bc_01JCXY",
"status": "completed",
"candidate_id": "cand-9911",
"created_at": "2026-08-28T12:56:44.642Z",
"completed_at": "2026-08-28T12:58:12.441Z",
"error": null,
"bypass": {
"detected": true,
"note": "Confirmed: Alex Rivera was found working at Acme Client Limited, another name for Acme Client Ltd, the company the candidate was put forward to in March 2025, having joined in April 2025.",
"matches": [
{
"submitted_company": "Acme Client Ltd",
"found_company": "Acme Client Limited",
"match_type": "variant",
"submitted_at": "2025-03",
"employment_start": "2025-04",
"timing": "joined_after_submission",
"confidence": "confirmed"
}
]
},
"talent_update": {
"identity": {
"confidence": "high",
"full_name": "Alex Rivera",
"headline": "Software Engineer at Acme Client Limited",
"current_employer": "Acme Client Limited",
"location": "London, UK"
},
"work_experience": [
{
"company": "Acme Client Limited",
"role": "Software Engineer",
"start_date": "2025-04",
"end_date": null,
"is_current": true,
"confidence": "high"
}
],
"education": [],
"online_presence": []
},
"credits": {
"balance": 140,
"plan_credits": 118,
"rollover_credits": 22,
"charged": 2,
"cached": false
},
"plan": {
"code": "growth",
"cycle": "monthly",
"credits_per_cycle": 200,
"requests_per_minute": 300,
"period_ends_at": "2026-10-28T12:00:00.000Z"
}
}Response fields
The response always includes a bypass object. When complete, a nested talent_update profile may also be present. GET responses also include the same additive credits and plan objects as create.
bypass.detectedtrue when at least one submission company matches a newly discovered employer.bypass.noteOne or two plain sentences for a person. They start with the review level (Confirmed, Likely or Needs review) or Not flagged, then explain why: where the candidate was found working, how it relates to the company you submitted to, and when they joined. null while the check is pending, or if the note could not be written.bypass.matches[]Evidence rows: submitted_company, found_company, match_type, submitted_at, employment_start, timing, confidence.talent_updateOptional nested profile (same shape as Talent Update) when identity resolution succeeded.creditsRemaining credits and charge for this check: balance, plan_credits, rollover_credits, charged, cached.planCurrent plan: code, cycle, credits_per_cycle, period_ends_at.timing compares submission month to employment start month: joined_after_submission, already_employed, or not_determinable when dates are missing. If the candidate started at the matched company more than a year before your submission, the match is not reported and bypass.note explains why.
confidence tells you how much review a match needs. confirmed: strongly corroborated, the company names align closely, and the employment start date falls after your submission. likely: corroborated, but the match is to a parent or subsidiary company, or the identity was less certain. needs_review: weaker corroboration, a sister company in the same group as the company you submitted to, no usable employment date, the candidate was already there before your submission, or lower identity confidence. Check a needs_review match yourself before acting on it.
Enumerations
Enums may gain new members over time.
statuspending, completed, failedbypass.matches[].match_typeexact, variant, groupbypass.matches[].timingjoined_after_submission, already_employed, not_determinablebypass.matches[].confidenceconfirmed, likely, needs_reviewplan.codefree, starter, growth, customplan.cyclemonthly, annual, nullErrors
Non-2xx responses use application/problem+json (RFC 9457).
Status
When
401
Missing or invalid API key.
402
Out of credits (<code>insufficient-credits</code>). Retrying will not help until you top up or upgrade.
404
Unknown check id or wrong partner.
422
Missing identity combination, submissions, or experience without profile_urls.
429
Rate limit exceeded (<code>rate-limit-exceeded</code>). Respect <code>Retry-After</code>, then retry.
500
Could not start or complete the check.
Example problem response
{
"type": "https://intelligence.tietalent.com/errors/invalid-request",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Provide a corporate email, or first_name and last_name together with company, employer_website or profile_urls, or a personal email with company or employer_website, or employer_website with role."
}Rate limits & versioning
Rate limits
Your plan sets how fast you may call the API, in requests per minute: Free 10, Starter 60, Growth 300. On a Custom plan the limit is agreed with you. One unit is one unit of work, so a batch spends one per candidate. Candidate Intelligence, Talent Update, Bypass Monitor and CSV enrichment share this single allowance. Successful responses and 429s carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429 adds Retry-After (seconds). Back off exponentially with jitter. See Candidate Intelligence › Rate limits for the full reference. Credits are a separate control: they meter how much you may consume, not how fast.
Retry-After: 42
Versioning
Version is in the path (/v1). Additive fields (including credits and plan) may appear without a version bump.
Webhooks
At-least-once delivery. Handlers must be idempotent on event id. Treat GET /api/v1/bypass-checks/{id} as authoritative at all times.
Event types
bypass_check_readyBypass check finished successfully.bypass_check_failedBypass check failed. Includes error_code in data.Signature verification
Every delivery carries an X-CI-Signature header: t=<unix>,v1=<hmac-sha256> over "{t}.{raw_body}", HMAC-SHA256 with your whsec_... secret. Same scheme as Candidate Intelligence and Talent Update.
X-CI-Signature: t=<unix>,v1=<hmac-sha256>
# Signed over "{t}.{raw_body}". Reject timestamps older than five minutes.Payload includes result_url, bypass_check_id, and candidate_id. Fetch full results via authed GET.
api_environment mirrors the label of the API key that authenticated the run (Production, Staging, or Test).
bypass_check_ready
{
"id": "evt_01JDABC",
"type": "bypass_check_ready",
"created_at": "2026-08-28T12:58:12.441Z",
"api_environment": "Production",
"data": {
"candidate_id": "cand-9911",
"status": "completed",
"result_url": "https://intelligence.tietalent.com/api/v1/bypass-checks/bc_01JCXY",
"bypass_check_id": "bc_01JCXY"
}
}bypass_check_failed
{
"id": "evt_01JDABD",
"type": "bypass_check_failed",
"created_at": "2026-08-28T12:58:15.441Z",
"api_environment": "Production",
"data": {
"candidate_id": "cand-9911",
"status": "failed",
"result_url": "https://intelligence.tietalent.com/api/v1/bypass-checks/bc_01JCXY",
"bypass_check_id": "bc_01JCXY",
"error_code": "internal_error"
}
}Known limits
Understanding these limits sets correct expectations.
- A check reports what can be attributed to your candidate. Where their identity cannot be confirmed the profile comes back empty, and the companies you submitted are still verified independently.
- Company matching uses fuzzy name resolution;
match_typeindicates match quality. - When
submitted_ator employment start is missing,timingisnot_determinable. - An employer already in your
experiencelist is still reported, because you may have recorded it after the candidate joined. It is returned asneeds_reviewunless the dates show they left and joined again after you submitted them, in which case it is scored normally. Only companies in yoursubmissionslist are ever reported. - A check with
bypass.detected: falsemeans no new employer matched a submission, not proof the candidate was not hired. - Maximum 100 submissions and 40 experience entries per candidate; 50 candidates per batch precompute.
Partner Program
Start integrating Bypass Monitor
Request access and we will activate your partner API credentials for bypass monitoring.