Skip to main content
PATCH
/
api
/
public
/
v1
/
job-drafts
/
{jobId}
curl -sS -X PATCH https://app.opentrain.ai/api/public/v1/job-drafts/<JOB_ID> \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "experienceLevel": "INTERMEDIATE",
    "paymentType": "PAY_PER_LABEL",
    "pricePerLabel": 0.06
  }'
{
  "ok": true,
  "jobId": "<JOB_ID>",
  "status": "DRAFT",
  "draftUrl": "https://app.opentrain.ai/job-post?jobId=<JOB_ID>",
  "validation": {
    "publishReady": true,
    "issueCount": 0,
    "missingFieldCount": 0,
    "missingFields": [],
    "issues": []
  }
}
Patches any subset of fields on an unpublished draft and returns refreshed validation. This is the second half of the gap-filling loop: take each missing field’s updateKeys and enumValues from the create response, ask your human the prompt, and patch the answers. Repeat until validation.publishReady is true, then publish. The accepted keys and enum values are machine-discoverable at GET /job-drafts/capabilities (draft.updateKeys and draft.enums). Requirements: jobs:write scope + the public_api_job_drafting feature. The job must be an unpublished draft you own — to edit a live job use PATCH /jobs/{id}.

Request

jobId
string
required
The draft job ID from the create response.
The body is a JSON object with one or more draft fields. Unknown keys are rejected (400 with zod issue details). Commonly patched keys:
jobTitle
string
Job title.
jobDescription
string
Full description text.
paymentType
string
PAY_PER_LABEL, PAY_PER_HOUR, or FIXED_PRICE. Pair with the matching rate field below.
pricePerHour
number
Hourly rate in USD (for PAY_PER_HOUR).
pricePerLabel
number
Per-label rate in USD (for PAY_PER_LABEL).
fixedPrice
number
Total fixed price in USD (for FIXED_PRICE).
experienceLevel
string
EXPERT, ENTRY_LEVEL, INTERMEDIATE, or ANY_EXPERIENCE_LEVEL.
dataVolumeUnit
string
HOURS_OF_RECORDING_AUDIO_VIDEO, NUMBER_OF_FILES, NUMBER_OF_WORDS, or UNKNOWN_NOT_SPECIFIED.
languages
string[]
Required languages.
countries
string[]
Allowed countries.
labelTypes
string[]
Label/annotation types.
headcount
number
Number of AI trainers to hire.
dataVolume
number
Quantity of data, in dataVolumeUnit units.
The full key list also includes labelingOverview, datasetDescription, dataType, subjectMatter, labelingSoftware, aiInterviewRequirements, budgetRange, workloadDesc, timeRequirement, projectDuration, freelancerType, projectScope, visibility, and more — fetch draft.updateKeys from capabilities for the authoritative set. Enum fields must use the exact canonical values above.

Response

ok
boolean
true on success.
jobId
string
The draft job ID.
status
string
Still DRAFT.
draftUrl
string
In-app URL of the draft editor.
validation
object
Refreshed validation — same shape as the create response: publishReady, issueCount, missingFieldCount, missingFields[] (with prompt/type/enumValues/updateKeys), issues[].

Errors

StatuscodeMeaning
400BAD_REQUESTEmpty body, invalid JSON, unknown keys or wrong types (details carries zod issues), or no fields set
401UNAUTHORIZEDMissing or invalid token
403FORBIDDENMissing jobs:write scope, feature disabled, or the job belongs to another account
404NOT_FOUNDNo such job
curl -sS -X PATCH https://app.opentrain.ai/api/public/v1/job-drafts/<JOB_ID> \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "experienceLevel": "INTERMEDIATE",
    "paymentType": "PAY_PER_LABEL",
    "pricePerLabel": 0.06
  }'
{
  "ok": true,
  "jobId": "<JOB_ID>",
  "status": "DRAFT",
  "draftUrl": "https://app.opentrain.ai/job-post?jobId=<JOB_ID>",
  "validation": {
    "publishReady": true,
    "issueCount": 0,
    "missingFieldCount": 0,
    "missingFields": [],
    "issues": []
  }
}