# Add a job to a sales order or quote

```http
POST https://secure.yoprint.com/v2/api/store/{team_slug}/v2/sales_order/{hash_id}/job
```

- API version: v2
- Operation ID: `v2_public_job_store`
- Section: Production › [Jobs](https://developer.yoprint.com/reference/jobs.md)
- Web page: https://developer.yoprint.com/reference/jobs/v2-job-store/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Adds a job to a sales order or quote. Add the job first, then add line items linked to it (`job: {"type": "linked", "id": ...}`): every line on the job takes its decoration price.

A job can carry, all optional:

- `service`: decoration services from Settings, each at a print location with one of the service's price options (services are not listed on the public API: copy `service_id` and `option` from an existing job or job preset);
- `pipeline`: work pipelines (production steps), each with a starting status;
- `mockup` and `production_file`: files you uploaded first, shown on the job.

Each `service`, `pipeline` and `mockup` entry needs `action: "create"`, or it is skipped silently; a `production_file` entry without `action` is refused with 422. To tie a mockup to a service, give the service a temporary `id` of your own (for example `tmpid-1`) and use it in the mockup's `job_service`.

The job gets the team's next job number and the order's production dates. The answer is the job.

Works on drafts and published orders. In any other state the call answers **HTTP 200** with `{"status": 500, "success": false, "error": {"code": "action_not_allowed", "message": null}}` and changes nothing: check `success`, not only the HTTP status. `{hash_id}` is the order's `id` (for example `V4keXwwxMQyXw7`); the `ord_` unique key is not accepted here.

## Permission

Needs permission to edit this sales order or quote:

- The team owner
- A role with `update_all_order`
- A role with `update_self_order`, when the order was created by or is assigned to the API key's user
- The person who created a draft, with `create_order`

Quotes use the same sales order permissions.

## Business rules

- Each `service`, `pipeline` and `mockup` entry needs `action: "create"`, or it is skipped silently. A `production_file` entry without `action` is refused with 422 `validation_failed`.
- A service must exist in the team's Settings ("Service Not Found") and `option` must be one of its options ("Service does not contain such option ...").
- A pipeline needs `pipeline_id` and `status_id`; its `start_date` and `due_date` go together.
- `scoped_id` is ignored: jobs always take the team's next job number.

## Side effects

- Recalculates the decoration pricing of the order's jobs and the order totals.
- Refreshes the order for people who have it open in the web app. On a published order, Customer Portal pages the customer has open refresh.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `hash_id` (string, required): The sales order's or quote's `id` (a hash id). The `ord_` unique key and the order number do not work here. Example `V4keXwwxMQyXw7`.

## Request body

Content type `application/json`, required.

Fields:

- `draft_session_id` (string, optional): Used by the web app's mockup designer. Leave it out.
- `draft_session_mockup` (array of objects, optional): Used by the web app's mockup designer. Leave it out.
- `has_service` (boolean, optional): Ignored; kept for older clients.
- `is_rush` (boolean, optional)
- `mockup` (array of objects, optional): Mockup images, uploaded first.
  - Each item:
    - `action` (string, required): Send `create` on every entry: a service, pipeline or mockup without it is skipped silently; a production file without it is refused. One of: `"create"`.
    - `id` (string, required): The upload's `id`.
    - `position` (integer, required)
    - `show_in_pdf` (boolean, required): Show it on the order PDF.
    - `job_service` (any, optional): The temporary service `id` (or ids) this mockup belongs to.
    - `name` (string, optional)
- `name` (string | null, optional): The job's name, for example "Front logo".
- `note` (string, optional): An internal note.
- `pipeline` (array of objects, optional): Work pipelines (production steps).
  - Each item:
    - `action` (string, required): Send `create` on every entry: a service, pipeline or mockup without it is skipped silently; a production file without it is refused. One of: `"create"`.
    - `pipeline_id` (string, required): A work pipeline id from Settings.
    - `status_id` (string, required): The pipeline's starting status id.
    - `assigned_users` (any, optional): A user id, or a list of user ids, to assign.
    - `due_date` (string (date-time) | null, optional): Due date (ISO 8601 date or date-time), or null. Send together with `start_date`.
    - `name` (string, optional)
    - `position` (integer, optional)
    - `start_date` (string (date-time) | null, optional): Start date (ISO 8601 date or date-time), or null. Send together with `due_date`.
- `position` (integer, optional): Where the job sits among the order's jobs. Leave it out to add it last.
- `production_file` (array of objects, optional): Production files, uploaded first.
  - Each item:
    - `action` (string, required): Send `create` on every entry: a service, pipeline or mockup without it is skipped silently; a production file without it is refused. One of: `"create"`.
    - `id` (string, required): The upload's `id`.
    - `position` (integer, required)
    - `job_service` (any, optional): The temporary service `id` (or ids) this file belongs to.
    - `name` (string, optional)
- `scoped_id` (string, optional): Accepted but ignored: the job gets the team's next job number. To change it afterwards, use Change a job number.
- `service` (array of objects, optional): Decoration services.
  - Each item:
    - `action` (string, required): Send `create` on every entry: a service, pipeline or mockup without it is skipped silently; a production file without it is refused. One of: `"create"`.
    - `service_id` (string, required): A decoration service id. Services are not listed on the public API: copy `service_id` from a job's `services` (Get one job of an order) or from a job preset (List job presets).
    - `customer_note` (string, optional): A note the customer sees.
    - `id` (string, optional): Your temporary id for this service, to reference it from mockups.
    - `location` (object, optional)
      - `id` (string, optional): A print location id.
      - `name` (string, optional): A custom location name.
      - `type` (string, optional): `database` for a saved print location (send `id`), `custom` for a free name (send `name`). One of: `"database"`, `"custom"`.
    - `note` (string, optional)
    - `option` (string, optional): One of the service's price option names, exactly as the service has it (the match is exact, case included), such as `2 Color`. Copy it from `services[].option` on an existing job or job preset. Another name, or none, is refused with 422 `job_validation_error` "Service does not contain such option …".
    - `position` (integer, optional): Order among the job's services. Required with `id`.

### Example request

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/job' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Front logo",
    "service": [
      {
        "action": "create",
        "id": "tmpid-1",
        "location": {
          "name": "Front",
          "type": "custom"
        },
        "option": "2 Color",
        "position": 1,
        "service_id": "15tmts0ZixfHL7"
      }
    ]
  }'
```

Another example: A job with a name only:

```bash
curl -X POST 'https://secure.yoprint.com/v2/api/store/your-team/v2/sales_order/V4keXwwxMQyXw7/job' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Team shirts"
  }'
```

## Response

`200` Successful response.

> **Warning: a 200 answer can still be a refusal.** Check `success` (and the fields named below) as well as the HTTP status.
> - The order is neither a draft nor published: `success: false`, `error.code` `action_not_allowed` (the body's `status` says 500). Fix: Check `success` in every reply, and the order's `internal_status`.

Refusal example (Refused with HTTP 200: `success` is false):

```json
{
  "error": {
    "code": "action_not_allowed",
    "message": null
  },
  "status": 500,
  "success": false
}
```

Example response:

```json
{
  "data": {
    "assigned_users": [],
    "id": "zmmzqMwhyJn20A",
    "is_rush": false,
    "mockup": [],
    "name": "Team shirts",
    "order_id": "V4keXwwxMQyXw7",
    "order_scoped_id": "SO1001",
    "pipelines": [],
    "pricing_total": {
      "dp": 6,
      "value": 0
    },
    "production_file": [],
    "scoped_id": "JOB-1003",
    "services": [],
    "status": {
      "color": "#9e9e9e",
      "customer_label": "Pending",
      "id": "YOqAGjroQEo2ZK",
      "label": "Artwork",
      "position": 1,
      "role": "job",
      "trashed": false,
      "type": "start"
    },
    "status_id": "YOqAGjroQEo2ZK"
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: The API key's role can't edit this order. Fix: Ask the team owner for sales order edit permission.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: `option` is not one of the service's option names, exactly: `job_validation_error` "Service does not contain such option …" (the name you sent). Fix: Copy the option name from `services[].option` on an existing job or job preset that uses the service.
  - When: The service is not one of the team's ("Service Not Found:…"), or a required field is missing (`job_validation_error`). Fix: Copy `service_id` from an existing job or job preset.

## Related

- [Add a job from a job template](https://developer.yoprint.com/reference/jobs/v2-job-create-with-template.md): `POST /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/job_template`
- [Add a line item to a sales order or quote](https://developer.yoprint.com/reference/sales-order-line-items/v2-order-item-store.md): `POST /v2/api/store/{team_slug}/v2/sales_order/{order_unique_key}/order_item`
- [Update a job](https://developer.yoprint.com/reference/jobs/v2-job-update.md): `PUT /v2/api/store/{team_slug}/v2/job/{hash_id}`
- [Upload a file](https://developer.yoprint.com/reference/uploads/v1-async-upload-store.md): `POST /v1/api/store/{team_slug}/async_upload`
- [Get one job of an order](https://developer.yoprint.com/reference/jobs/v2-job-show.md): `GET /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/job/{job_id}`
- [List job presets](https://developer.yoprint.com/reference/job-presets/v1-job-preset-index.md): `GET /v1/api/store/{team_slug}/setting/job_preset`
