> ## Documentation Index
> Fetch the complete documentation index at: https://opentrain.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Proposal

> Read one proposal in depth: bid, AI-interview score and summary, location/identity verification, assessment results, and contract state.

Reads a single proposal in full — everything from [`GET /jobs/{id}/proposals`](/docs/developers/api-reference/jobs/list-proposals) plus the AI-interview block, location/identity verification, labeling-assessment results, and the contract created if you already hired. This is the deep-dive step of the [candidate evaluation flow](/docs/developers/guides/evaluate-candidates).

Candidate identity stays masked pre-hire (first name + last initial). Resume files, personal emails, phone numbers, and payment details are never returned — see [privacy](/docs/developers/concepts/privacy-and-work-email). For the full interview transcript, use [`GET /proposals/{proposalId}/interview`](/docs/developers/api-reference/proposals/interview).

**Requirements:** `proposals:read` scope. The proposal must be on a job you own (`403` otherwise). Works pre-claim.

## Request

<ParamField path="proposalId" type="string" required>
  The proposal ID, e.g. from the [proposals list](/docs/developers/api-reference/jobs/list-proposals).
</ParamField>

## Response

<ResponseField name="proposal" type="object">
  <Expandable title="proposal" defaultOpen>
    <ResponseField name="id" type="string">
      Proposal ID.
    </ResponseField>

    <ResponseField name="jobId" type="string">
      The job this proposal belongs to.
    </ResponseField>

    <ResponseField name="jobTitle" type="string">
      Job title (denormalized for convenience).
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      ISO submission timestamp.
    </ResponseField>

    <ResponseField name="updatedAt" type="string">
      ISO last-change timestamp.
    </ResponseField>

    <ResponseField name="status" type="object">
      `{raw, label}` — machine value (e.g. `SHORTLISTED`) plus display label.
    </ResponseField>

    <ResponseField name="bid" type="object">
      `{amountUsd, unit, labelerHourlyRateUsd}` — the candidate's asking rate and its unit.
    </ResponseField>

    <ResponseField name="candidate" type="object">
      Privacy-safe candidate summary: `{id, profileSlug, displayName, firstName, lastNameInitial, profileTitle, profilePhotoUrl, countryCode, country, talentType, highestEarningsUsd, reviewCount}` — same shape as the [proposals list](/docs/developers/api-reference/jobs/list-proposals). Use `id` or `profileSlug` with [`GET /freelancers/{idOrSlug}`](/docs/developers/api-reference/freelancers/get) for the full profile.
    </ResponseField>

    <ResponseField name="metrics" type="object">
      `{interviewScore, matchScore}` — AI-interview score and job-match score, `null` when not available.
    </ResponseField>

    <ResponseField name="aiInterview" type="object | null">
      `null` when the candidate has not taken the AI interview.

      <Expandable title="aiInterview">
        <ResponseField name="score" type="number | null">
          Normalized interview score.
        </ResponseField>

        <ResponseField name="summary" type="string | null">
          AI-written summary of the interview.
        </ResponseField>

        <ResponseField name="transcriptAvailable" type="boolean">
          `true` when a transcript exists — fetch it via [`GET /proposals/{proposalId}/interview`](/docs/developers/api-reference/proposals/interview).
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="verification" type="object">
      <Expandable title="verification">
        <ResponseField name="location" type="object">
          `{status, method, checkedAt}` — `status` is `PASSED`, `FAILED`, or `NOT_RECORDED`; `method` is `GPS`, `ID_DOCUMENT`, or `MIXED_SIGNALS` (`null` when `NOT_RECORDED`); `checkedAt` is an ISO timestamp or `null`.
        </ResponseField>

        <ResponseField name="identity" type="object">
          `{verified}` — `true` when the candidate completed identity verification.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="openLabel" type="object | null">
      Latest result from OpenTrain's built-in labeling assessment, when the candidate completed one for this proposal: `{status, score, employerRating, completedAt}` (each `null`-able). `null` when no assessment exists.
    </ResponseField>

    <ResponseField name="contract" type="object | null">
      `{id, status}` with `status` of `ACTIVE` or `ENDED` when you already hired from this proposal — track it via [`GET /contracts/{id}`](/docs/developers/api-reference/contracts/get). `null` pre-hire.
    </ResponseField>
  </Expandable>
</ResponseField>

## Errors

| Status | `code`         | Meaning                                                                     |
| ------ | -------------- | --------------------------------------------------------------------------- |
| `400`  | `BAD_REQUEST`  | Missing `proposalId`                                                        |
| `401`  | `UNAUTHORIZED` | Missing or invalid token                                                    |
| `403`  | `FORBIDDEN`    | Missing `proposals:read` scope, or the proposal is on another account's job |
| `404`  | `NOT_FOUND`    | No such proposal                                                            |

<RequestExample>
  ```bash curl theme={null}
  curl -sS https://app.opentrain.ai/api/public/v1/proposals/<PROPOSAL_ID> \
    -H "Authorization: Bearer $OT_API_TOKEN"
  ```

  ```bash CLI theme={null}
  opentrain proposals get --proposal-id <PROPOSAL_ID> --json
  ```

  ```json MCP: opentrain_get_proposal theme={null}
  {
    "proposalId": "<PROPOSAL_ID>"
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "proposal": {
      "id": "<PROPOSAL_ID>",
      "jobId": "<JOB_ID>",
      "jobTitle": "Spanish Sentiment Labeling — Social Media Posts",
      "createdAt": "2026-06-11T15:40:00.000Z",
      "updatedAt": "2026-06-12T09:05:00.000Z",
      "status": {
        "raw": "SHORTLISTED",
        "label": "Shortlisted"
      },
      "bid": {
        "amountUsd": 0.06,
        "unit": "PER_LABEL",
        "labelerHourlyRateUsd": null
      },
      "candidate": {
        "id": "<FREELANCER_ID>",
        "profileSlug": "maria-g",
        "displayName": "Maria G.",
        "firstName": "Maria",
        "lastNameInitial": "G",
        "profileTitle": "Text Annotation Specialist",
        "profilePhotoUrl": "https://app.opentrain.ai/<PHOTO_PATH>",
        "countryCode": "ES",
        "country": "Spain",
        "talentType": "FREELANCER",
        "highestEarningsUsd": 2400,
        "reviewCount": 7
      },
      "metrics": {
        "interviewScore": 86,
        "matchScore": 0.91
      },
      "aiInterview": {
        "score": 86,
        "summary": "Strong grasp of sentiment-labeling edge cases; clear communication; native Spanish speaker with prior social-media annotation experience.",
        "transcriptAvailable": true
      },
      "verification": {
        "location": {
          "status": "PASSED",
          "method": "GPS",
          "checkedAt": "2026-06-11T15:38:00.000Z"
        },
        "identity": {
          "verified": true
        }
      },
      "openLabel": null,
      "contract": null
    }
  }
  ```
</ResponseExample>
