Screen candidates
Run a resume through ProBB AI, handle a report that needs review, invite the candidate to an AI interview and read the evaluation.
This guide follows one candidate from resume to decision. The steps are the same whether a recruiter clicks through the app or your software calls the API. It assumes you have a job and a key with report and interview scopes. See authentication.
resume ──> report ──> (needs_review?) ──> interview ──> evaluation ──> decision1. Check the price
Reports and interviews spend credits or part of a monthly allowance. Before you start, read what they cost.
curl "$PROBB_URL/api/v1/organization" -H "Authorization: Bearer $PROBB_API_KEY"prices.report and prices.interview are the cost of one, in credits. Pass maxCredits on every spending call so a price change cannot surprise you. See credits and prices.
2. Create the report
A report is one analysis of one candidate against one job. You have two ways to create it.
In one call, with an inline candidate. This is the shortest path. ProBB AI finds the candidate by externalId or email, or creates them.
curl -X POST "$PROBB_URL/api/v1/reports" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"jobId": "JOB_ID",
"candidate": {
"name": "Ada Lovelace",
"email": "ada@example.com",
"externalId": "ats-4821",
"resume": { "fileId": "FILE_ID" }
},
"externalId": "app-9931",
"maxCredits": 5
}'With a candidate you already pushed. Send candidateId instead of candidate. Use this in a sync, where candidates exist before anyone applies to a job. See keep an ATS in sync.
A resume is a PDF or DOCX file you uploaded with POST /api/v1/files, or plain text. See the quickstart for the upload.
The answer is 202 Accepted with the report queued. Possible refusals:
| Code | Meaning |
|---|---|
job_closed | The job is closed. Reopen it first. |
candidate_resume_missing | The candidate has no resume. Add one with PUT /api/v1/candidates/{id}/resume. |
report_exists | This candidate and job already have a report. existingId points to it. |
price_exceeds_max | The price is above your maxCredits. Nothing was charged. |
insufficient_credits, quota_exceeded | Not enough credits or allowance. |
If the resume is still being read, the report waits as queued and starts by itself when the candidate is ready. If the resume cannot be read, the report becomes failed with candidate_resume_failed.
3. Follow the report
A report moves through these statuses:
queued ──> processing ──> completed
├──> needs_review ──> processing (proceed) ──> completed
│ └──> completed (dismiss)
└──> failedDo not poll in a tight loop. Add a webhook endpoint for report.completed, report.needs_review and report.failed. Each event carries the report. If you have to poll, wait a few seconds between calls, and read GET /api/v1/events?types=report.completed,report.needs_review,report.failed once instead of one GET per report.
A finished report has a score from 0 to 10 and a summary. Add ?include=evaluation for the rubric, highlights, follow-up questions, integrity checks and policy notes. Show the evidence beside the score. A score is a starting point for a person, not a hiring decision.
When a report needs review
needs_review means ProBB AI stopped before the full analysis because the resume does not look like it fits the job. A person must decide. You can decide through the API.
| Action | What happens |
|---|---|
proceed | Runs the full analysis anyway, as Proceed anyway does in the app. It is charged like a new report. You can send maxCredits. |
dismiss | Accepts the cautious report as it stands. It becomes completed with the score it already has, with no further analysis. |
curl -X POST "$PROBB_URL/api/v1/reports/$REPORT_ID/review" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "proceed", "maxCredits": 5 }'Any other status is 409 report_not_reviewable.
When a report fails
Read failureCode and fix the cause, for example by reopening a closed job. Then delete the failed report and create it again. A failed report blocks a new report for the same candidate and job until you delete it with DELETE /api/v1/reports/{id}. Credits already spent are not refunded.
4. Invite the candidate to an interview
You can invite the candidate of any completed report. Anything else is 409 report_not_completed.
curl -X POST "$PROBB_URL/api/v1/interviews" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "reportId": "REPORT_ID", "sendEmail": true, "maxCredits": 10 }'The answer is 202 with the interview invited. ProBB AI prepares the interview in the background. Credits are held now.
Who sends the invitation
sendEmail: true(the default): ProBB AI emails the candidate. The address is theemailyou send, else the candidate's email. With none, the call fails withcandidate_email_missing. Checkinvitation.emailStatusforqueued,sentorfailed.sendEmail: false: nothing is emailed. The response carriesinvitationUrl, the candidate's personal link. It is in that response only. Send anIdempotency-Key, so a retry returns it again. Anyone with the link can take the interview, so treat it like a password.
Invite several candidates at once
POST /api/v1/interviews/batch invites the candidates of up to 10 completed reports in one call, as the bulk invite in the app does. Only reports that can be invited are: one that already has an interview, is on a closed job, has no email on file, or whose analysis has not finished comes back skipped with the reason, and is never charged. Probb always emails these invitations to the address it has on file.
curl -X POST "$PROBB_URL/api/v1/interviews/batch" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "reportIds": ["REPORT_1", "REPORT_2", "REPORT_3"], "maxCredits": 30 }'maxCredits caps the total for the candidates who would be invited; a higher price fails the whole call with price_exceeds_max and nothing is charged. The answer is 202 with one result per report, in the order you sent them: invited (with the interview), skipped (with a reason), failed, or not_attempted. Each interview is admitted separately: if credits or the monthly limit run out part-way, the interviews already created stay, the one that hit the limit is failed and the rest are not_attempted. Send an Idempotency-Key so a retry returns the same results instead of inviting again.
Resend, cancel and replace
POST /api/v1/interviews/{id}/resendsends the email again while the candidate has not started. Each interview can be sent 3 times an hour, in the app and the API together.POST /api/v1/interviews/{id}/cancelstops an interview the candidate has not started. The link stops working and held credits are released.POST /api/v1/interviews/{id}/retry-scoringqueues the evaluation of a finished interview again after it failed (evaluation_failed). It costs nothing, and an interview whose scoring did not fail is409 interview_scoring_not_failed.- A report that already has an interview is
409 interview_exists. SendreplaceExisting: trueto replace it. The old one becomescancelled. An interview the candidate is taking right now cannot be replaced.
5. Follow the interview
invited ──> in_progress ──> evaluating ──> completed
├──> expired
├──> cancelled
└──> failedSubscribe to interview.started, interview.completed, interview.expired and interview.failed. An interview that failed has a failureCode of preparation_failed, evaluation_failed or invitation_failed. Of these, invitation_failed can be sent again with resend and evaluation_failed can be scored again with retry-scoring.
6. Read the evaluation
curl "$PROBB_URL/api/v1/interviews/$INTERVIEW_ID?include=evaluation" \
-H "Authorization: Bearer $PROBB_API_KEY"Once the interview is completed, evaluation holds:
recommendationandsummary: the AI's advice and overall feedback.scores: core skill, problem solving, communication and role fit out of 10, and readiness out of 100. A score can benull. That means it was not measured, not that it is zero.coverage: how much evidence the interview produced. Read it before you trust a score.requirements,strengths,concernsandevidence: what the candidate showed, with excerpts.integrity: signals that deserve a second look. They help a person review. They are not proof of anything.limitations: what the evaluation cannot tell you.
Transcripts and recordings are not available through the API. Reviewers read them in the app.
7. Record the decision
The AI gives a recommendation. The hiring team makes the decision. They are different things, and the API keeps them apart.
curl -X PUT "$PROBB_URL/api/v1/jobs/$JOB_ID/candidates/$CANDIDATE_ID/decision" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "decision": "advance", "note": "Strong system design answers." }'A decision is advance, hold, not_progressing or hired. It belongs to the candidate and job, so it stays when a report is replaced. Every change is kept in the decision's history.
Look in your talent pool first
Before you wait for applicants, check the candidates you already have. A talent search ranks them against a job. It is free, uses no credits and runs in the background for a few minutes. A workspace can have 3 searches running and start 20 an hour; beyond that you get 429 rate_limit_exceeded, so reuse a recent search instead of refreshing. The key needs jobs:write and candidates:read.
curl -X POST "$PROBB_URL/api/v1/jobs/$JOB_ID/talent-searches" \
-H "Authorization: Bearer $PROBB_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'If a search for the job is already running, or one finished in the last 7 days, you get that one back with "reused": true. Send "refresh": true to start a new one. A closed job takes no search (409 job_closed). A job that was only just created answers 409 job_not_indexed while ProBB AI prepares it. Try again in a few minutes.
Poll GET /api/v1/talent-searches/{id} until done is true. Each match has the candidate, a band, how many must-have and important requirements their resume covers, and a short highlight. A match is a suggestion from a resume already in ProBB AI. To analyse one, create a report with that candidate's ID. If a report for the job already exists, report holds its ID.
Look a person up by name with GET /api/v1/search?q=priya. To follow a batch of reports someone started in the app, read GET /api/v1/activities.
Handling many applicants
Create a report for each applicant. There are no batch calls. A workspace runs 10 reports at once, and the rest wait as queued and start by themselves, so you can send them as fast as the rate limits allow. Subscribe to report.completed and let events drive the next step, instead of waiting in a loop.