Invite candidate
curl --request POST \
--url https://api.interviewflowai.com/api/external/ingest/candidate \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"name": "<string>",
"email": "<string>",
"jobId": "<string>",
"status": "<string>",
"resumeUrl": "<string>",
"skills": [
"<string>"
],
"customQuestions": [
{}
],
"additionalFields": {},
"metadata": {}
}
'import requests
url = "https://api.interviewflowai.com/api/external/ingest/candidate"
payload = {
"name": "<string>",
"email": "<string>",
"jobId": "<string>",
"status": "<string>",
"resumeUrl": "<string>",
"skills": ["<string>"],
"customQuestions": [{}],
"additionalFields": {},
"metadata": {}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
email: '<string>',
jobId: '<string>',
status: '<string>',
resumeUrl: '<string>',
skills: ['<string>'],
customQuestions: [{}],
additionalFields: {},
metadata: {}
})
};
fetch('https://api.interviewflowai.com/api/external/ingest/candidate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interviewflowai.com/api/external/ingest/candidate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'email' => '<string>',
'jobId' => '<string>',
'status' => '<string>',
'resumeUrl' => '<string>',
'skills' => [
'<string>'
],
'customQuestions' => [
[
]
],
'additionalFields' => [
],
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interviewflowai.com/api/external/ingest/candidate"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interviewflowai.com/api/external/ingest/candidate")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interviewflowai.com/api/external/ingest/candidate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_bodyAPI Integration
Invite candidate
Invite one candidate and receive their interview link.
POST
/
api
/
external
/
ingest
/
candidate
Invite candidate
curl --request POST \
--url https://api.interviewflowai.com/api/external/ingest/candidate \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: <api-key>' \
--data '
{
"name": "<string>",
"email": "<string>",
"jobId": "<string>",
"status": "<string>",
"resumeUrl": "<string>",
"skills": [
"<string>"
],
"customQuestions": [
{}
],
"additionalFields": {},
"metadata": {}
}
'import requests
url = "https://api.interviewflowai.com/api/external/ingest/candidate"
payload = {
"name": "<string>",
"email": "<string>",
"jobId": "<string>",
"status": "<string>",
"resumeUrl": "<string>",
"skills": ["<string>"],
"customQuestions": [{}],
"additionalFields": {},
"metadata": {}
}
headers = {
"X-API-KEY": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-KEY': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
email: '<string>',
jobId: '<string>',
status: '<string>',
resumeUrl: '<string>',
skills: ['<string>'],
customQuestions: [{}],
additionalFields: {},
metadata: {}
})
};
fetch('https://api.interviewflowai.com/api/external/ingest/candidate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.interviewflowai.com/api/external/ingest/candidate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'name' => '<string>',
'email' => '<string>',
'jobId' => '<string>',
'status' => '<string>',
'resumeUrl' => '<string>',
'skills' => [
'<string>'
],
'customQuestions' => [
[
]
],
'additionalFields' => [
],
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-KEY: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.interviewflowai.com/api/external/ingest/candidate"
payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-KEY", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.interviewflowai.com/api/external/ingest/candidate")
.header("X-API-KEY", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.interviewflowai.com/api/external/ingest/candidate")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-KEY"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"email\": \"<string>\",\n \"jobId\": \"<string>\",\n \"status\": \"<string>\",\n \"resumeUrl\": \"<string>\",\n \"skills\": [\n \"<string>\"\n ],\n \"customQuestions\": [\n {}\n ],\n \"additionalFields\": {},\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_bodyUse this API to invite one candidate at a time. The endpoint creates the interview record and returns the direct interview link.
All requests must include your API key.
Rate limit: 100 requests per minute per API key. Requests over the limit return
429 Too Many Requests.Endpoint
POST https://api.interviewflowai.com/api/external/ingest/candidate
X-API-KEY: your_api_key_here
Content-Type: application/json
Request body fields
string
required
Candidate’s full name. Blank names are rejected.
string
required
Candidate’s email address. The value is normalized to lowercase.
string
required
The unique ID of the Interviewer to create the interview under.
string
shortlisted or draft. If omitted, public Interviewers default to shortlisted and private Interviewers default to draft.string
A publicly accessible URL to the Candidate’s resume (PDF). InterviewFlowAI downloads and stores the file.
string[]
Skill names used to generate skill-based custom interview questions.
object[]
Candidate-specific custom questions to attach to the interview.
object
Custom Candidate fields configured in InterviewFlowAI.
object
External reference data such as ATS IDs or phone number.
Example request
curl -X POST "https://api.interviewflowai.com/api/external/ingest/candidate" \
-H "Content-Type: application/json" \
-H "X-API-KEY: your_api_key_here" \
-d '{
"name": "Jane Doe",
"email": "jane.doe@example.com",
"jobId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"status": "shortlisted",
"metadata": {
"externalCandidateId": "candidate_12345"
}
}'
Example response
{
"data": {
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"jobId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"name": "Jane Doe",
"email": "jane.doe@example.com",
"status": "shortlisted",
"createdAt": "2026-07-01T10:30:00.000Z",
"interviewLink": "https://app.interviewflowai.com/form?interviewId=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
},
"code": 201,
"message": "Interview link created successfully"
}
Behavior
| Scenario | Behavior |
|---|---|
| Candidate already has an interview for the same Interviewer | The request is rejected and no new interview is created. |
status is shortlisted | The returned link takes the candidate directly to the interview flow. |
status is draft | The interview is created as a draft candidate record. |
Interviewer is public and status is omitted | The interview is created as shortlisted. |
Interviewer is private and status is omitted | The interview is created as draft. |
Use this endpoint when your ATS, CRM, or internal system should decide how and when to share the interview link with the candidate.
⌘I

