Skip to main content
POST
/
api
/
public
/
v1
/
jobs
/
{id}
/
invites
curl -sS -X POST https://app.opentrain.ai/api/public/v1/jobs/<JOB_ID>/invites \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"freelancerId": "<FREELANCER_ID>"}'
{
  "ok": true,
  "proposalId": "<PROPOSAL_ID>",
  "jobId": "<JOB_ID>",
  "freelancerId": "<FREELANCER_ID>",
  "alreadyInvited": false
}
Invites a specific AI trainer to a published job of yours, creating an invited proposal they can respond to. Find candidates via GET /freelancers/{idOrSlug} or from proposals on other jobs. Idempotent per freelancer: re-inviting returns 200 with alreadyInvited: true instead of creating a duplicate. Requirements: proposals:write scope + the public_api_hiring feature + a claimed account (unclaimed accounts get 403 with details.reason: "account_claim_required" and a claimUrl). The job must be yours and published.

Request

id
string
required
Your published job’s ID.
freelancerId
string
required
The AI trainer’s user ID.

Response

201 when a new invite was created, 200 when the freelancer was already invited.
ok
boolean
true on success.
proposalId
string
The invited proposal’s ID — track it via GET /proposals/{id}.
jobId
string
The job ID.
freelancerId
string
The invited AI trainer’s user ID.
alreadyInvited
boolean
true when this invite already existed (idempotent repeat).

Errors

StatuscodeMeaning
400BAD_REQUESTMissing or invalid freelancerId (zod details)
401UNAUTHORIZEDMissing or invalid token
403FORBIDDENMissing proposals:write scope, public_api_hiring disabled, or account not claimed (details.reason: "account_claim_required", details.claimUrl)
404NOT_FOUNDNo such job (or not yours), or no such user
409CONFLICTInvite not possible — details.reason is one of job_not_published, not_a_freelancer, freelancer_unavailable
curl -sS -X POST https://app.opentrain.ai/api/public/v1/jobs/<JOB_ID>/invites \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"freelancerId": "<FREELANCER_ID>"}'
{
  "ok": true,
  "proposalId": "<PROPOSAL_ID>",
  "jobId": "<JOB_ID>",
  "freelancerId": "<FREELANCER_ID>",
  "alreadyInvited": false
}