Ajouter des candidats
curl --request POST \
--url https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone_number": "+33687654321"
}
]
}
'import requests
url = "https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates"
payload = { "candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone_number": "+33687654321"
}
] }
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({
candidates: [
{
first_name: 'Jean',
last_name: 'Dupont',
phone_number: '+33612345678',
email: 'jean.dupont@example.com'
},
{first_name: 'Marie', last_name: 'Martin', phone_number: '+33687654321'}
]
})
};
fetch('https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates', 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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates",
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([
'candidates' => [
[
'first_name' => 'Jean',
'last_name' => 'Dupont',
'phone_number' => '+33612345678',
'email' => 'jean.dupont@example.com'
],
[
'first_name' => 'Marie',
'last_name' => 'Martin',
'phone_number' => '+33687654321'
]
]
]),
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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates")
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 \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"campaign_id": "abc123def456",
"candidates_added": 23,
"candidates_skipped": 2,
"calls_queued": 23,
"errors": [
{
"index": 5,
"error": "Numéro de téléphone invalide"
}
]
}
Candidats
Ajouter des candidats
Ajoute jusqu’à 100 candidats à une campagne et les contacte selon contact_mode : appel immédiat (call), SMS d’invitation (sms) ou email d’invitation (email), avec failover SMS/email
POST
/
api
/
v1
/
campaigns
/
{campaign_id}
/
candidates
Ajouter des candidats
curl --request POST \
--url https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone_number": "+33687654321"
}
]
}
'import requests
url = "https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates"
payload = { "candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone_number": "+33687654321"
}
] }
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({
candidates: [
{
first_name: 'Jean',
last_name: 'Dupont',
phone_number: '+33612345678',
email: 'jean.dupont@example.com'
},
{first_name: 'Marie', last_name: 'Martin', phone_number: '+33687654321'}
]
})
};
fetch('https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates', 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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates",
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([
'candidates' => [
[
'first_name' => 'Jean',
'last_name' => 'Dupont',
'phone_number' => '+33612345678',
'email' => 'jean.dupont@example.com'
],
[
'first_name' => 'Marie',
'last_name' => 'Martin',
'phone_number' => '+33687654321'
]
]
]),
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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates"
payload := strings.NewReader("{\n \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\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://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.voicehire.io/api/v1/campaigns/{campaign_id}/candidates")
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 \"candidates\": [\n {\n \"first_name\": \"Jean\",\n \"last_name\": \"Dupont\",\n \"phone_number\": \"+33612345678\",\n \"email\": \"jean.dupont@example.com\"\n },\n {\n \"first_name\": \"Marie\",\n \"last_name\": \"Martin\",\n \"phone_number\": \"+33687654321\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"campaign_id": "abc123def456",
"candidates_added": 23,
"candidates_skipped": 2,
"calls_queued": 23,
"errors": [
{
"index": 5,
"error": "Numéro de téléphone invalide"
}
]
}
Paramètres
string
required
Identifiant unique de la campagne
Corps de la requête
string
default:"call"
Méthode de contact :
call(défaut) — Appel téléphonique automatique immédiat.emailrequis pour chaque candidat.sms— SMS d’invitation à passer l’entretien, avec lien personnalisé (0,25 crédit). Le candidat clique et lance lui-même l’entretien quand il le souhaite. Failover email : si lephoneest absent ou invalide mais qu’unemailvalide est fourni, l’invitation part par email (gratuit).email— Email d’invitation à passer l’entretien, avec lien personnalisé (gratuit). Failover SMS : si l’emailest absent ou invalide mais qu’unphonevalide est fourni, l’invitation part par SMS (0,25 crédit).
sms et email, chaque candidat doit fournir au moins une coordonnée valide (phone ou email), sinon il est rejeté.array
required
Liste des candidats à ajouter (maximum 100)
Show Propriétés de chaque candidat
Show Propriétés de chaque candidat
string
required
Prénom du candidat
string
required
Nom de famille du candidat
string
Numéro de téléphone (format E.164, principalement FR). Requis si
contact_mode = "call". Pour sms/email, au moins un de phone ou email doit être valide.Formats acceptés :061234567806 12 34 56 78+33612345678
string
Adresse email du candidat. Requis si
contact_mode = "call". Pour sms/email, au moins un de phone ou email doit être valide (le failover bascule automatiquement sur le canal disponible).Réponse
{
"campaign_id": "abc123def456",
"candidates_added": 23,
"candidates_skipped": 2,
"calls_queued": 23,
"errors": [
{
"index": 5,
"error": "Numéro de téléphone invalide"
}
]
}
L’exemple ci-dessus correspond au mode
call. En modes sms et email, la réponse a la forme : candidates_sourced, candidates_skipped, sms_sent, emails_sent, errors.string
required
Identifiant de la campagne
integer
required
Nombre de candidats ajoutés avec succès
integer
required
Nombre de candidats ignorés (déjà présents dans la campagne)
integer
required
Nombre d’appels mis en file d’attente
array
Exemple de requête
curl -X POST https://app.voicehire.io/api/v1/campaigns/abc123def456/candidates \
-H "X-API-Key: vh_live_XXXXXXXXXXXXX" \
-H "Content-Type: application/json" \
-d '{
"candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone": "0612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone": "0687654321",
"email": "marie.martin@example.com"
}
]
}'
import requests
response = requests.post(
"https://app.voicehire.io/api/v1/campaigns/abc123def456/candidates",
headers={
"X-API-Key": "vh_live_XXXXXXXXXXXXX",
"Content-Type": "application/json"
},
json={
"candidates": [
{
"first_name": "Jean",
"last_name": "Dupont",
"phone": "0612345678",
"email": "jean.dupont@example.com"
},
{
"first_name": "Marie",
"last_name": "Martin",
"phone": "0687654321",
"email": "marie.martin@example.com"
}
]
}
)
const axios = require('axios');
const response = await axios.post(
'https://app.voicehire.io/api/v1/campaigns/abc123def456/candidates',
{
candidates: [
{
first_name: 'Jean',
last_name: 'Dupont',
phone: '0612345678',
email: 'jean.dupont@example.com'
},
{
first_name: 'Marie',
last_name: 'Martin',
phone: '0687654321',
email: 'marie.martin@example.com'
}
]
},
{
headers: {
'X-API-Key': 'vh_live_XXXXXXXXXXXXX',
'Content-Type': 'application/json'
}
}
);
Notes importantes
- Les candidats déjà présents dans la campagne (même téléphone ou même email) sont ignorés (skipped)
- Maximum 100 candidats par requête
- En modes
smsetemail, un failover s’applique par candidat : si le canal principal n’a pas de coordonnée valide, l’autre canal est utilisé s’il est disponible. Le coût facturé correspond au canal réellement utilisé. - Le coût en crédits dépend du canal effectivement utilisé :
| Canal utilisé | Action | Coût par candidat traité avec succès |
|---|---|---|
call (défaut) | Appel téléphonique automatique immédiat | 1 crédit (transaction_type = campaign) |
sms | SMS d’invitation avec lien personnalisé | 0,25 crédit (transaction_type = sourcing) |
email | Email d’invitation avec lien personnalisé | Gratuit |
Codes d’erreur
| Code | Description |
|---|---|
| 400 | Données invalides (format incorrect, champs manquants) |
| 401 | Clé API manquante ou invalide |
| 402 | Crédits insuffisants |
| 404 | Campagne non trouvée |
Authorizations
Clé API au format vh_live_XXXXX
Path Parameters
Identifiant de la campagne
Body
application/json
Required array length:
1 - 100 elementsShow child attributes
Show child attributes
Méthode de contact :
call(défaut) : appel téléphonique automatique immédiat.emailrequis par candidat.sms: SMS d'invitation à passer l'entretien (0,25 crédit). Failover email : si lephoneest absent/invalide mais qu'unemailvalide est fourni, l'invitation part par email (gratuit).email: email d'invitation à passer l'entretien (gratuit). Failover SMS : si l'emailest absent/invalide mais qu'unphonevalide est fourni, l'invitation part par SMS (0,25 crédit).
Pour sms et email, chaque candidat doit fournir au moins une coordonnée valide (phone OU email), sinon il est rejeté.
Available options:
call, sms, email 
