# Using the Patch API

> Change parts of a resume with JSON Patch (RFC 6902) operations instead of sending the whole resume.

The Patch API lets you make small, targeted changes to your resume without sending the entire data object. Instead of replacing the whole resume with a `PUT`, you send a list of **JSON Patch** operations that describe exactly what to change.

This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/html/rfc6902) standard.

## When to use PATCH or PUT

| Use case                                     | Method    |
| -------------------------------------------- | --------- |
| Update a single field (e.g., name, headline) | **PATCH** |
| Add or remove an item in a section           | **PATCH** |
| Change template, colors, or fonts            | **PATCH** |
| Replace the entire resume data at once       | **PUT**   |

<Info>
  The PATCH endpoint only changes the resume's `data`: its content and design. To change the resume's `name`, `slug`,
  `tags` or `isPublic`, use `PUT /resumes/{id}`.
</Info>

## Authentication

All requests require your API key in the `x-api-key` header. See [Using the API](https://docs.hiresweep.com/guides/using-the-api) for how to create one.

<Info>
  The API is served under `https://hiresweep.com/api/openapi`.
</Info>

## Endpoint

```text
PATCH /api/openapi/resumes/{id}
```

### Request body

The resume ID is taken from the URL path. The body has the `operations` array and, optionally, `expectedUpdatedAt`:

```json
{
  "operations": [{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }],
  "expectedUpdatedAt": "2026-10-10T09:30:00.000Z"
}
```

| Field               | Required | Description                                                                                                   |
| ------------------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `operations`        | Yes      | One or more JSON Patch operations, applied in order.                                                          |
| `expectedUpdatedAt` | No       | The resume's `updatedAt` from when you read it. The patch applies only if the resume hasn't changed since; otherwise it fails with `409`. |

Each operation is an object with the following properties:

| Property | Required                     | Description                                                                     |
| -------- | ---------------------------- | ------------------------------------------------------------------------------- |
| `op`     | Yes                          | The operation to perform: `add`, `remove`, `replace`, `move`, `copy`, or `test` |
| `path`   | Yes                          | A JSON Pointer (RFC 6901) to the target location in the resume data             |
| `value`  | For `add`, `replace`, `test` | The value to use for the operation                                              |
| `from`   | For `move`, `copy`           | A JSON Pointer to the source location                                           |

## Examples

### Replace a basic field

Update the resume holder's name and headline:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "replace", "path": "/basics/name", "value": "Jane Doe" },
      { "op": "replace", "path": "/basics/headline", "value": "Senior Software Engineer" }
    ]
  }'
```

### Add an experience entry

Append a new item to the experience section:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      {
        "op": "add",
        "path": "/sections/experience/items/-",
        "value": {
          "id": "a1b2c3d4-0000-0000-0000-000000000000",
          "hidden": false,
          "company": "Acme Corp",
          "position": "Staff Engineer",
          "location": "San Francisco, CA",
          "period": "Jan 2024 - Present",
          "website": { "url": "https://acme.example.com", "label": "Acme Corp" },
          "description": "<p>Leading the platform team.</p>"
        }
      }
    ]
  }'
```

<Tip>
  The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array". To
  insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning.
</Tip>

### Remove an item from a section

Remove the second skill (index `1`) from the skills section:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "remove", "path": "/sections/skills/items/1" }
    ]
  }'
```

### Update metadata (template, colors, fonts)

Switch the template and update the primary color:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "replace", "path": "/metadata/template", "value": "bronzor" },
      { "op": "replace", "path": "/metadata/design/colors/primary", "value": "rgba(37, 99, 235, 1)" }
    ]
  }'
```

### Test, then replace

The `test` operation checks that a value matches before proceeding. If the test fails, the entire patch is rejected. This is useful to avoid overwriting changes made by another client:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "test", "path": "/basics/name", "value": "Albert Einstein" },
      { "op": "replace", "path": "/basics/name", "value": "Jane Doe" }
    ]
  }'
```

If `/basics/name` is not `"Albert Einstein"` at the time of the request, the entire patch fails with a `400` error and no changes are applied.

### Only patch the version you read

To make sure nobody changed the resume between your read and your patch, for example in the builder, send the `updatedAt` value from `GET /resumes/{id}` as `expectedUpdatedAt`:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "expectedUpdatedAt": "2026-10-10T09:30:00.000Z",
    "operations": [
      { "op": "replace", "path": "/basics/headline", "value": "Staff Engineer" }
    ]
  }'
```

If the resume changed since then, the request fails with `409 RESUME_VERSION_CONFLICT` and nothing is applied. The error's `data.updatedAt` holds the current version: read the resume again, rebuild your operations and retry.

### Move an item within a section

Move the first experience item to the third position:

```bash
curl -X PATCH "https://hiresweep.com/api/openapi/resumes/YOUR_RESUME_ID" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "move", "from": "/sections/experience/items/0", "path": "/sections/experience/items/2" }
    ]
  }'
```

## Error handling

Error responses are JSON with a `code` field that names the error.

| Status | Code                       | Description                                                                                                               |
| ------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `INVALID_PATCH_OPERATIONS` | The operations are structurally invalid, target a path that doesn't exist, fail a `test`, or produce resume data that fails schema validation. |
| `401`  | `UNAUTHORIZED`             | The API key is missing, wrong or expired.                                                                                 |
| `404`  | `NOT_FOUND`                | The resume doesn't exist or doesn't belong to you.                                                                        |
| `409`  | `RESUME_VERSION_CONFLICT`  | You sent `expectedUpdatedAt` and the resume has changed since.                                                            |
| `500`  | `RESUME_LOCKED`            | The resume is locked. Unlock it first.                                                                                    |
| `429`  | `TOO_MANY_REQUESTS`        | More than 300 changes to this resume in a minute. See [rate limits](https://docs.hiresweep.com/guides/using-the-api#rate-limits).                   |

<Warning>
  All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the
  operations are applied.
</Warning>

## Tips

- **Fetch first, then patch.** Use `GET /resumes/{id}` to inspect the current structure before crafting your operations. This helps you target the correct paths and array indices.
- **Guard against concurrent edits.** Send `expectedUpdatedAt`, or combine `test` and `replace` when you expect a field to hold a specific value.
- **Batch related changes.** You can send multiple operations in a single request. They are applied in order, so later operations can depend on earlier ones.
- **The `-` index appends.** When adding items to arrays, use `-` as the index (e.g., `/sections/skills/items/-`) to append to the end.
