Skip to main content
POST
/
v1
/
score
Score a candidate
curl --request POST \
  --url https://embed.nova.dweet.com/v1/score \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --header 'X-Tenant-Id: <x-tenant-id>' \
  --data '
{
  "jobId": "job-123",
  "applicationId": "app-456",
  "candidate": {
    "resumeUrl": "https://storage.example.com/resumes/abc123.pdf?token=xyz&expires=1705312800",
    "applicationAnswers": [
      {
        "question": {
          "text": "Are you authorized to work in the UK?",
          "type": "boolean",
          "required": true
        },
        "answer": "Yes"
      },
      {
        "question": {
          "text": "Years of backend development experience?",
          "type": "dropdown",
          "options": [
            "0-2 years",
            "2-5 years",
            "5-10 years",
            "10+ years"
          ]
        },
        "answer": "5-10 years"
      }
    ]
  }
}
'
{
"scoringJobId": "sj_abc123def456",
"status": "queued",
"estimatedCompletionSeconds": 30
}
Submit a candidate for AI-powered scoring. Criteria are automatically loaded from your stored criteria for this job - you don’t need to pass them. This is an asynchronous operation with results delivered via webhook.

How It Works

Request

{
  "jobId": "job-123",
  "applicationId": "app-456",
  "candidate": {
    "resumeUrl": "https://storage.example.com/resumes/abc123.pdf?token=xyz&expires=1705312800"
  }
}
Notice there’s no criteria field - Nova automatically uses the criteria you generated for this job via POST /v1/criteria/generate.

Prerequisites

Before scoring candidates, you must have active criteria for the job:
// First, generate criteria for the job (once per job)
await fetch('https://embed.nova.dweet.com/v1/criteria/generate', {
  method: 'POST',
  headers: { ... },
  body: JSON.stringify({
    jobContext: {
      jobId: 'job-123',
      jobTitle: 'Senior Software Engineer',
      companyName: 'Acme Corp',
      jobDescription: '...',
    },
  }),
});

// Then score candidates - criteria are loaded automatically
await fetch('https://embed.nova.dweet.com/v1/score', {
  method: 'POST',
  headers: { ... },
  body: JSON.stringify({
    jobId: 'job-123',
    applicationId: 'app-456',
    candidate: {
      resumeUrl: 'https://...',
    },
  }),
});
If no active criteria exist for the job, the request will fail with CRITERIA_NOT_FOUND. Generate criteria first using /v1/criteria/generate.

Request Requirements

Resume URL

The resumeUrl must be:
  • Pre-signed: Accessible without authentication
  • Valid for 2+ hours: We recommend 24 hours for safety
  • HTTPS: HTTP URLs are not accepted
"candidate": {
  "resumeUrl": "https://storage.example.com/resumes/abc123.pdf?token=xyz&expires=1705312800"
}
See Supported Formats for accepted file types.

Optional: Parsed Resume

If you have structured resume data, include it to improve accuracy:
"candidate": {
  "resumeUrl": "https://...",
  "parsedResume": {
    "name": "Jane Smith",
    "experience": [
      {
        "title": "Senior Engineer",
        "company": "Tech Corp",
        "duration": "2019-2024"
      }
    ],
    "skills": ["Go", "Python", "Kubernetes"]
  }
}

Optional: Application Answers

Include application Q&A for additional context:
"candidate": {
  "resumeUrl": "https://...",
  "applicationAnswers": {
    "workAuthorization": "Yes, I am authorized to work in the UK",
    "startDate": "Immediately available",
    "salaryExpectations": "80,000 - 100,000"
  }
}

Idempotency

The scoring endpoint is idempotent by default. Duplicate requests (same jobId + applicationId) return the existing job instead of creating a new one.
This makes retries safe. To bypass idempotency (e.g., after updating criteria), use the rescore parameter. See Idempotency for details.

Response

{
  "scoringJobId": "sj_abc123def456",
  "status": "queued",
  "estimatedCompletionSeconds": 30
}
Store the scoringJobId to:
  • Correlate with incoming webhooks
  • Poll for results if needed
  • Debug issues with support

Processing Time

StageDuration
Queue waitTypically < 5 seconds
Processing20-30 seconds
Total~30 seconds

What Happens Next

Results arrive via webhook:
{
  "event": "score.completed",
  "scoringJobId": "sj_abc123def456",
  "jobId": "job-123",
  "applicationId": "app-456",
  "result": {
    "score": 7,
    "assessment": {
      "verdict": "Strong candidate with solid backend experience...",
      "strengths": ["6 years of backend engineering experience"],
      "concerns": ["No direct Kubernetes experience"],
      "interviewFocus": ["Probe depth of distributed systems knowledge"]
    }
  },
  "completedAt": "2025-01-15T10:30:45Z"
}

Criteria Updates

If you update criteria after scoring some candidates:
  • Existing scores retain the criteria version used at scoring time
  • New scores use the updated criteria
  • Each score is linked to its specific criteria version for audit purposes
See Criteria Management for how to update criteria.

Re-scoring Candidates

To re-score a candidate (e.g., after updating criteria), set rescore: true:
{
  "jobId": "job-123",
  "applicationId": "app-456",
  "candidate": {
    "resumeUrl": "https://..."
  },
  "rescore": true
}
This bypasses idempotency and creates a new scoring job using the current criteria.
Re-scoring counts as an additional billable score. Idempotent retries (same IDs without rescore: true) are not charged again.
See Idempotency for details.

Batch Scoring

For scoring multiple candidates at once, use POST /v1/score/batch:
{
  "jobId": "job-123",
  "candidates": [
    {
      "applicationId": "app-001",
      "candidate": { "resumeUrl": "https://..." }
    },
    {
      "applicationId": "app-002",
      "candidate": { "resumeUrl": "https://..." }
    }
  ]
}
  • Up to 100 candidates per batch
  • All candidates must be for the same job
  • Results delivered individually via webhook
  • Supports rescore: true at the batch level
See Score Batch for details.

Retrieving Scores

You can retrieve scores two ways:
MethodEndpointUse Case
By scoring job IDGET /v1/score/{scoringJobId}When you have Nova’s ID from the submission response
By application IDGET /v1/jobs/{jobId}/applications/{applicationId}/scoreWhen you only have your own application ID
See Get Score by Scoring Job ID or Get Score by Application ID for details.

Next Step

Set up your Webhook Endpoint to receive results.

Authorizations

Authorization
string
header
required

API key authentication. Use your environment-specific API key (sk_test_* for sandbox, sk_live_* for production).

Headers

X-Tenant-Id
string
required

Your customer/tenant identifier. Used for data isolation and per-customer metrics. Tenants are created automatically on first request.

Body

application/json
jobId
string
required

Your job ID. Must have active criteria stored (via /v1/criteria/generate).

applicationId
string
required

Your application ID (echoed in webhook)

candidate
object
required
language
string
default:English

Language for assessment output

rescore
boolean
default:false

When true, bypasses idempotency and creates a new scoring job even if this application was previously scored. Use after updating criteria to re-score existing candidates. Counts as an additional billable score.

Response

Scoring job accepted and queued

scoringJobId
string
required

Unique identifier for this scoring job

status
enum<string>
required

Initial status is always 'queued'

Available options:
queued
estimatedCompletionSeconds
integer

Estimated time until completion