Skip to main content

Create Candidate

NL EN

Updated June 15, 2026

POST /api/v1/candidates
curl -X POST \
  "https://app.recruitsome.com/api/v1/candidates" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json"
const response = await fetch('https://app.recruitsome.com/api/v1/candidates', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Accept': 'application/json',
  },
});

const data = await response.json();
console.log(data);
use Illuminate\Support\Facades\Http;

$response = Http::withToken('YOUR_API_KEY')
    ->acceptJson()
    ->post('https://app.recruitsome.com/api/v1/candidates');

$data = $response->json();

Create a new candidate by uploading their resume. The resume is processed asynchronously in the background — the API immediately returns a processing_id you can log as a reference for support questions (there is no progress-lookup endpoint). Once processing is complete, the responsible user receives an assistant request notification within the application.

Authentication

Requires an API key with the candidates:write scope (included in the Chrome Plugin preset). Calls with a key that lacks the scope return a 403 with error code INSUFFICIENT_SCOPE.

How It Works

The candidate creation process is asynchronous and follows this flow:

  1. Upload: You submit the resume via the API
  2. Accepted: The API returns 202 Accepted with a processing_id
  3. Processing: AI extracts candidate information from the resume in the background
  4. Notification: The user specified by user_id receives an assistant request in the application with the result

This is the same processing pipeline used when uploading a resume through the application UI. The candidate is created, enriched, and the user is notified through the normal assistant request flow.

Simple Example

curl -X POST https://app.recruitsome.com/api/v1/candidates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 1,
    "resume": {
      "filename": "john_doe_cv.pdf",
      "content": "JVBERi0xLjQKJeLj...",
      "mime_type": "application/pdf"
    }
  }'
// Read file and convert to base64
async function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result.split(',')[1]);
    reader.onerror = reject;
  });
}

const resumeFile = document.getElementById('resume-input').files[0];
const resumeBase64 = await fileToBase64(resumeFile);

const response = await fetch('https://app.recruitsome.com/api/v1/candidates', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    user_id: 1,
    resume: {
      filename: resumeFile.name,
      content: resumeBase64,
      mime_type: resumeFile.type
    }
  })
});

if (response.status === 202) {
  const result = await response.json();
  console.log('Processing started:', result.data.processing_id);
}
import requests
import base64

# Read resume file
with open('resume.pdf', 'rb') as f:
    resume_content = base64.b64encode(f.read()).decode('utf-8')

response = requests.post(
    'https://app.recruitsome.com/api/v1/candidates',
    headers={
        'Authorization': 'Bearer YOUR_API_KEY',
        'Content-Type': 'application/json'
    },
    json={
        'user_id': 1,
        'resume': {
            'filename': 'resume.pdf',
            'content': resume_content,
            'mime_type': 'application/pdf'
        }
    }
)

if response.status_code == 202:
    result = response.json()
    print('Processing started:', result['data']['processing_id'])

Response Example

JSON
{
  "message": "Resume uploaded successfully. The candidate will be created in the background.",
  "data": {
    "processing_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
}

With Intake Creation

If the tenant has the Intake feature enabled, you can request that an intake is automatically created for the new candidate:

Bash
curl -X POST https://app.recruitsome.com/api/v1/candidates \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 1,
    "resume": {
      "filename": "jane_smith_cv.pdf",
      "content": "JVBERi0xLjQKJeLj...",
      "mime_type": "application/pdf"
    },
    "create_intake": true
  }'

The create_intake flag is only effective when the tenant has the Intake feature enabled. If the feature is not enabled, this parameter is silently ignored.

Request Fields

Required Fields

FieldTypeDescription
user_idintegerRequired. The tenant user ID to associate the candidate with. This user will receive the assistant request notification.
resumeobjectRequired. The resume document to process
resume.filenamestringRequired. Original filename (max 255 chars)
resume.contentstringRequired. Base64 encoded file content
resume.mime_typestringRequired. MIME type of the file

Optional Fields

FieldTypeDescription
create_intakebooleanRequest automatic intake creation for the new candidate (requires Intake feature)

Resume Document

The resume must be base64 encoded and include metadata:

JSON
{
  "filename": "resume.pdf",
  "content": "base64_encoded_content_here",
  "mime_type": "application/pdf"
}

Supported Document Types

Allowed MIME TypesMax Size
application/pdf10MB
application/msword (.doc)10MB
application/vnd.openxmlformats-officedocument.wordprocessingml.document (.docx)10MB
application/zip10MB
application/x-ole-storage10MB
application/octet-stream10MB

The application/zip, application/x-ole-storage, and application/octet-stream types are accepted because browsers and older Word files often misreport .doc/.docx MIME types — pass the type your file API gives you. The file itself should still be a PDF or Word document for the AI processing to succeed.

Warning

Base64 encoding increases file size by approximately 33%. A 10MB file will be approximately 13.3MB when base64 encoded.

Error Handling

Validation Errors (422)

JSON
{
  "message": "The given data was invalid.",
  "errors": {
    "user_id": ["The specified user does not exist."],
    "resume": ["A resume document is required to create a candidate."],
    "resume.mime_type": ["The resume must be a PDF or Word document (application/pdf, application/msword, or application/vnd.openxmlformats-officedocument.wordprocessingml.document)."]
  }
}

The resume.mime_type message names the primary document types. The endpoint also accepts the fallback MIME types listed under Supported Document Types above — you only see this error when the type is outside the full list.

Invalid Base64 Content (422)

JSON
{
  "message": "The given data was invalid.",
  "errors": {
    "resume.content": ["The resume content is not valid base64 encoded data."]
  }
}

File Size Exceeded (422)

JSON
{
  "message": "The given data was invalid.",
  "errors": {
    "resume": ["The resume file size exceeds the maximum allowed limit of 10MB."]
  }
}

Rate Limiting (429)

The endpoint accepts 10 requests per minute per API key. Exceeding it returns the standard throttle response with a Retry-After header (seconds until the window resets):

JSON
{
  "message": "Too Many Attempts."
}

Server Error (500)

JSON
{
  "message": "An unexpected error occurred while processing the resume.",
  "error": "PROCESSING_FAILED"
}

What Happens After Submission

Once the API returns 202 Accepted, the following happens in the background:

  1. Resume Analysis: AI extracts personal information, work experience, education, and skills from the resume
  2. Duplicate Detection: The system checks if a candidate with the same email already exists
  3. Candidate Creation: If the candidate is new, their profile is created with the extracted data
  4. Enrichment Workflow: Additional AI processing enriches the candidate profile (tag analysis, summary generation)
  5. Notification: The user specified by user_id receives an assistant request with the result

Possible Outcomes

ScenarioWhat Happens
New candidateCandidate is created, user receives "candidate created" notification
Existing candidate (same email)Resume is attached for review, user receives "resume review" notification
Missing email in resumeCandidate is saved as "pending", user receives "needs completion" notification
Processing failureUser receives "processing failed" notification

Tip

All outcomes result in an assistant request notification to the user — you don't need to poll for status. The user can take appropriate action directly in the application.

Notes

  • The user_id must reference a valid user within the tenant. This user becomes the "owner" of the candidate and receives all notifications.
  • Processing usually completes within a few minutes, depending on resume complexity
  • The processing_id is a UUID that can be stored for reference, though the primary notification mechanism is the assistant request
  • Remember to handle personal data according to GDPR requirements