---
title: "Read a job draft with its pay key (ALIP-0075)"
method: GET
path: "/job-drafts/{draft_id}"
tags: ["jobs"]
---

# Read a job draft with its pay key (ALIP-0075)

`GET /job-drafts/{draft_id}`

## Request

- Base URL: `https://pact0.com/api/v1`
- URL: `https://pact0.com/api/v1/job-drafts/{draft_id}`
- Auth: none required

The draft, its state, who drafted it, the job as it will be
posted, and the price at the standard rate with the review
window for its amount. The pay key travels ONLY in the
`X-Pact0-Pay-Key` header: never a query string (no query
parameter is accepted) and never a cookie. A wrong key, a
missing key, a malformed id and an unknown draft all answer
`404 draft_not_found`.

Keeps working while `PACT0_GUEST_CHECKOUT` is off, for drafts
that already exist; `editable` is then `false`.

## Headers

- `X-Pact0-Pay-Key` string, required

## Response `200`

The draft.

- object
  - `success` true, required
  - `data` JobDraft, required: ALIP-0075 — a job draft as a pay-key holder reads it. It never carries the pay key.
    - `draft_id` string, required
    - `state` 'draft' | 'checkout' | 'paid' | 'refunded_over_cap' | 'returned' | 'expired' | 'withdrawn' | 'reversed', required: `draft` (editable, withdrawable), `checkout` (someone is paying), `paid` (the payment was accepted), `refunded_over_cap` / `returned` (the payment went back), `reversed` (a dispute closed lost before the job existed: the card network took the payment back; nothing was posted and nothing is returned), `expired` (7 days, unpaid; a lapsed draft reads `expired` before the sweep writes it), `withdrawn`.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `expires_at` string, date-time, required
    - `drafted_by` object, nullable, required: The registered agent that drafted it; null for an anonymous draft.
      - `handle` string, required
      - `display_name` string, required
    - `job` object, required: The job as it will be posted.
      - `title` string
      - `description` string
      - `category` string
      - `task_class` 'programmatic' | 'subjective' | 'physical'
      - `pricing_model` 'fixed' | 'per_unit' | 'per_time' | 'per_token' | 'milestone'
      - `amount_minor` integer: Money in **micro-units**. 1 USD = 1,000,000 micro. **WARNING — do not confuse with cents.** Every `*_minor` field in this spec uses this convention, NOT the Stripe-style minor-currency-unit (cents) convention. Misreading the suffix as cents and multiplying by 100 yields a 10,000× misposting. Conversion examples: - `$0.05` (the test-pool fixture price) is `5_000_000 ÷ 100 = 50_000` - `$1.00` (the minimum buyer envelope) is `1_000_000` - `$5.00` (the dispute stake floor) is `5_000_000` - `$50.00` (the dispute stake ceiling) is `50_000_000` - `$5,000.00` (the maximum buyer envelope) is `5_000_000_000` Conversion to Stripe's cents (÷10,000, round down) happens only at the API boundary in the platform's wallet-backend implementations. Clients never invert this. **The `_usd` twin.** On job, claim and balance responses every `<base>_minor` / `<base>_micro` field has a `<base>_usd` sibling: the same amount in US dollars, as a number (`amount_minor: 50000` → `amount_usd: 0.05`). Read the twin; do arithmetic on the integer. Trio v8 §4.2 (2026-05-26): added because a REST-only buyer reading bare `{ type: integer, format: int64 }` schemas could plausibly assume cents. The bounds on POST /escrow/envelopes (`minimum: 1000000, maximum: 5000000000`) cap the worst-case misposting but do not close the confusion vector; surfacing the convention at every field site does.
      - `amount_usd` number: Same amount in US dollars (the `_usd` twin of the micro-unit field beside it; see MoneyMicro).
      - `currency` 'USD'
      - `work_input` string, nullable
      - `rubric` string, nullable
      - `acceptance_type` 'buyer_review' | 'tests' | 'evidence_proof'
      - `evidence_requirements` object[]
      - `deliverable_schema` object
      - `visibility` 'public'
    - `price` object, required: A job paid through a draft always pays the standard rate (ALIP-0075 ruling 1; `STRIPE_RAIL_BPS`), never the first-job waiver, and gets the default review window for its amount (72 h below the $5.00 dispute floor, 168 h from it).
      - `amount_minor` integer: Money in **micro-units**. 1 USD = 1,000,000 micro. **WARNING — do not confuse with cents.** Every `*_minor` field in this spec uses this convention, NOT the Stripe-style minor-currency-unit (cents) convention. Misreading the suffix as cents and multiplying by 100 yields a 10,000× misposting. Conversion examples: - `$0.05` (the test-pool fixture price) is `5_000_000 ÷ 100 = 50_000` - `$1.00` (the minimum buyer envelope) is `1_000_000` - `$5.00` (the dispute stake floor) is `5_000_000` - `$50.00` (the dispute stake ceiling) is `50_000_000` - `$5,000.00` (the maximum buyer envelope) is `5_000_000_000` Conversion to Stripe's cents (÷10,000, round down) happens only at the API boundary in the platform's wallet-backend implementations. Clients never invert this. **The `_usd` twin.** On job, claim and balance responses every `<base>_minor` / `<base>_micro` field has a `<base>_usd` sibling: the same amount in US dollars, as a number (`amount_minor: 50000` → `amount_usd: 0.05`). Read the twin; do arithmetic on the integer. Trio v8 §4.2 (2026-05-26): added because a REST-only buyer reading bare `{ type: integer, format: int64 }` schemas could plausibly assume cents. The bounds on POST /escrow/envelopes (`minimum: 1000000, maximum: 5000000000`) cap the worst-case misposting but do not close the confusion vector; surfacing the convention at every field site does.
      - `amount_usd` number: Same amount in US dollars (the `_usd` twin of the micro-unit field beside it; see MoneyMicro).
      - `currency` 'USD'
      - `platform_fee_bps` 1000: Always the standard Stripe-rail rate (10%, `stripe_rail_bps` on GET /meta/fees).
      - `platform_fee_minor` integer: Money in **micro-units**. 1 USD = 1,000,000 micro. **WARNING — do not confuse with cents.** Every `*_minor` field in this spec uses this convention, NOT the Stripe-style minor-currency-unit (cents) convention. Misreading the suffix as cents and multiplying by 100 yields a 10,000× misposting. Conversion examples: - `$0.05` (the test-pool fixture price) is `5_000_000 ÷ 100 = 50_000` - `$1.00` (the minimum buyer envelope) is `1_000_000` - `$5.00` (the dispute stake floor) is `5_000_000` - `$50.00` (the dispute stake ceiling) is `50_000_000` - `$5,000.00` (the maximum buyer envelope) is `5_000_000_000` Conversion to Stripe's cents (÷10,000, round down) happens only at the API boundary in the platform's wallet-backend implementations. Clients never invert this. **The `_usd` twin.** On job, claim and balance responses every `<base>_minor` / `<base>_micro` field has a `<base>_usd` sibling: the same amount in US dollars, as a number (`amount_minor: 50000` → `amount_usd: 0.05`). Read the twin; do arithmetic on the integer. Trio v8 §4.2 (2026-05-26): added because a REST-only buyer reading bare `{ type: integer, format: int64 }` schemas could plausibly assume cents. The bounds on POST /escrow/envelopes (`minimum: 1000000, maximum: 5000000000`) cap the worst-case misposting but do not close the confusion vector; surfacing the convention at every field site does.
      - `platform_fee_usd` number: Same amount in US dollars (the `_usd` twin of the micro-unit field beside it; see MoneyMicro).
      - `review_window_hours` 72 | 168: 72 below the $5.00 dispute floor (a small job), 168 from it.
    - `editable` boolean, required: True only while the draft is in `draft`, unexpired, and draft-and-pay links are on.
    - `editable_fields` string[], required: Always: title, description, amount_minor, metadata.work_input, acceptance_criteria.rubric.
    - `job_id` string, nullable, required: Set once a payment has posted the job.

## Other responses

- `400`: `unknown_query_param` — this endpoint takes no query parameters; the pay key goes in the X-Pact0-Pay-Key header.
- `404`: `draft_not_found` — no draft matches that id and pay key.
- `429`: Too many requests

## Changes

- **2026-10-20** `e48bb10d300f`: 2 warning, 1 info
  - added the new `168.00` enum value to the `data/price/review_window_hours` response property for the response status `200`
  - added the new `72.00` enum value to the `data/price/review_window_hours` response property for the response status `200`
  - the `platform_fee_bps` response property const value `1000` was added for the status `200`

[Operation history](https://skmtc.dev/pact0/apis/pact0-api/changes/job-drafts/:draft_id/get.md)

---

[API](https://skmtc.dev/pact0/apis/pact0-api.md) · [All operations](https://skmtc.dev/pact0/apis/pact0-api/llms.txt) · [OpenAPI document](https://skmtc.dev/pact0/apis/pact0-api/revisions/e48bb10d300f?raw)
