Contents

v1

Partner 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

1

Get approved and receive an API key

Request access from TieTalent. Once approved, you receive an X-API-Key for your integration.

2

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).

3

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.

4

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.

5

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

Send your partner API key in the 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.

POST/api/v1/bypass-checks
{
  "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.

Batch precompute accepts up to 50 candidates per request via the 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

202
{
  "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.

202
{
  "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, failed
bypass.matches[].match_typeexact, variant, group
bypass.matches[].timingjoined_after_submission, already_employed, not_determinable
bypass.matches[].confidenceconfirmed, likely, needs_review
plan.codefree, starter, growth, custom
plan.cyclemonthly, annual, null

Errors

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.

Webhook endpoints are configured during partner onboarding.

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=&lt;unix&gt;,v1=&lt;hmac-sha256&gt; 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_type indicates match quality.
  • When submitted_at or employment start is missing, timing is not_determinable.
  • An employer already in your experience list is still reported, because you may have recorded it after the candidate joined. It is returned as needs_review unless the dates show they left and joined again after you submitted them, in which case it is scored normally. Only companies in your submissions list are ever reported.
  • A check with bypass.detected: false means 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.

Contact Partnerships