Skip to content

Store an edited resume as a job's tailored resume

PUT
/api/tailored-resumes
curl --request PUT \
--url http://127.0.0.1:8765/api/tailored-resumes \
--header 'Content-Type: application/json' \
--data '{ "actor": "agent", "job_url": "https://boards.greenhouse.io/acme/jobs/101", "markdown": "## Summary\n\n- Platform engineer with nine years building billing systems.\n\n## Experience\n\n### Northwind Health\nStaff Engineer | Jun 2020 - Present\n\n- Rebuilt the scheduling service on Python and Postgres.\n" }'

Store an edited resume markdown as one job’s tailored resume (no model call). Attaches the markdown to that one job (and profile) as its tailored resume; other jobs and the profile’s resume are untouched. A line that is unchanged from the stored tailored resume keeps its sources; a line that is a resume line is a copy; any other body line becomes kind custom (your own text, no source cited). A custom line is checked, with your whole resume and every answer as the sources: a name or contact detail answers 422 personal_info_refused; a number, a skill the posting names, or a Skills item that neither the resume nor an answer states answers 422 edited_resume_unsupported (save an answer that states it with POST /api/answers, then send the markdown again). An entry heading (employer, title, dates) must be a resume line, unchanged. A refusal lists every problem by the markdown’s line number, never a line’s text. Lines above the first ## section are not stored. The response is the stored resume with edited (written_by operator or agent, edited_at, source), changed (false when it already was the stored one) and recheck: the job is queued in the pipeline, whose tailor step keeps an edited resume, so the re-assessment, the Scout ATS score and the Scout label run against it (result enqueued, or not_queued with error_code, e.g. assessment_missing; runner false: run gigai scout pipeline run --once). Background tailoring never replaces an edited resume; POST /api/tailored-resumes does. The markdown also goes to the job’s folder of the jobs folder as resume.md (GET /api/jobs-folder). Markdown that carries the revision comment of the job brief (<!-- gigai-resume: updated_at=... sha256=... -->) is refused with 409 resume_file_stale when the stored resume is another version than the one it names (read the brief again, then edit), and a stored resume file that cannot be read is not written over (409 stored_resume_unreadable). Storing an edit drops a proposed resume that was waiting for the job.

Media typeapplication/json
object
actor

Who writes: recorded as written_by. Also the X-GigAI-Actor header. Without either, a write from the Scout UI (a browser page of this server) is the operator’s and any other loopback write is the agent’s.

string
Allowed values: operator agent
job_url
required

The posting’s link: the ONE job this resume is for.

string
markdown
required

Resume markdown in GigAI’s format (what a tailored resume’s markdown and the resumes folder’s file hold), at most 65536 bytes.

string
profile_id

The profile it was tailored for. Default: the selected profile.

string
source

Free text, at most 300 characters: where the edit came from. Stored in edited.source; never sent to a model.

string
Example
{
"actor": "agent",
"job_url": "https://boards.greenhouse.io/acme/jobs/101",
"markdown": "## Summary\n\n- Platform engineer with nine years building billing systems.\n\n## Experience\n\n### Northwind Health\nStaff Engineer | Jun 2020 - Present\n\n- Rebuilt the scheduling service on Python and Postgres.\n"
}

Success.

Media typeapplication/json
object
schema_version
required
Allowed value: scout-tailor-response:1
key
additional properties
any
Example
{
"changed": true,
"edited": {
"edited_at": "2026-10-04T10:05:00Z",
"source": null,
"written_by": "agent"
},
"job": {
"job_identity": "https://boards.greenhouse.io/acme/jobs/101"
},
"markdown": "## ...",
"recheck": {
"error_code": null,
"result": "enqueued",
"runner": true
},
"schema_version": "scout-tailor-response:1"
}

Forbidden / forbidden_origin: loopback peer and Host 127.0.0.1:<port> or localhost:<port> only (Origin too on writes).

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Examplegenerated
{
"error": {
"allowed_keys": [
"example"
],
"code": "example",
"message": "example"
}
}

Profile_not_found, target_unavailable

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Example
{
"error": {
"code": "profile_not_found",
"message": "..."
}
}

Resume_file_stale, stored_resume_unreadable

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Example
{
"error": {
"code": "resume_file_stale",
"message": "..."
}
}

Unsupported_media_type: writes need Content-Type: application/json.

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Examplegenerated
{
"error": {
"allowed_keys": [
"example"
],
"code": "example",
"message": "example"
}
}

Unknown_key, wrong_type, invalid_value, resume_markdown_invalid, resume_markdown_too_large, personal_info_refused, edited_resume_unsupported

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Example
{
"error": {
"code": "unknown_key",
"message": "..."
}
}

Job_fetch_failed

Media typeapplication/json
object
error
required
object
allowed_keys
Array<string>
code
required
string
message
required
string
key
additional properties
any
Example
{
"error": {
"code": "job_fetch_failed",
"message": "..."
}
}