> For the complete documentation index, see [llms.txt](https://docs.shortcutco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.shortcutco.com/developers/api-reference/reference/jobs.md).

# Jobs

The recurring or one-time work you sold.

A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.

Fields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job).

## List jobs

> Jobs newest first, or by last change when \`updatedSince\` is set. \`externalSource\` and \`externalId\` together narrow the list to the job with that id in your system; a pair that still names a job a price change replaced is 409 \`job\_superseded\`, naming the current job. A price change in the app replaces a job with a new one; both are listed, linked by \`previousVersionJobId\`, and the earlier one has an \`effectiveUntilDate\`.\
> \
> Returns a list of \[job objects]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`job\_superseded\`]\(<https://docs.shortcutco.com/developers/concepts/errors#job\\_superseded>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs":{"get":{"operationId":"jobs.list","tags":["Jobs"],"summary":"List jobs","description":"Jobs newest first, or by last change when `updatedSince` is set. `externalSource` and `externalId` together narrow the list to the job with that id in your system; a pair that still names a job a price change replaced is 409 `job_superseded`, naming the current job. A price change in the app replaces a job with a new one; both are listed, linked by `previousVersionJobId`, and the earlier one has an `effectiveUntilDate`.\n\nReturns a list of [job objects](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`job_superseded`](https://docs.shortcutco.com/developers/concepts/errors#job_superseded). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"limit","in":"query","required":false,"description":"How many items to return, 1-200. Defaults to 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `meta.nextCursor` from the previous page. Omit it for the first page.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"customerId","in":"query","required":false,"description":"Only this customer's jobs.","schema":{"type":"string","format":"uuid"}},{"name":"propertyId","in":"query","required":false,"description":"Only jobs at this property.","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","required":false,"description":"Only jobs with this status.","schema":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"]}},{"name":"teamId","in":"query","required":false,"description":"Only jobs this crew is doing: jobs whose `effectiveTeamId` is this crew (the next visit's own crew, or the job's crew when that visit has none or there is no upcoming visit).","schema":{"type":"string","format":"uuid"}},{"name":"externalSource","in":"query","required":false,"description":"With `externalId`: only the job with this id in your system.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$"}},{"name":"externalId","in":"query","required":false,"description":"With `externalSource`: only the job with this id in your system.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[^\\p{Cc}\\s/\\\\]+$"}},{"name":"updatedSince","in":"query","required":false,"description":"ISO 8601 timestamp. Returns jobs changed at or after it, oldest change first. Keep the largest `updatedAt` you have seen, subtract five minutes, and pass it on the next sync; deduplicate by `id`. A job's `updatedAt` changes only when the job itself changes: `nextVisitDate`, `nextVisitId`, `effectiveTeamId`, and `address` come from its visits and property, so sync visits from their own list.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"meta":{"type":"object","properties":{"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` to get the next page; null on the last page."},"hasMore":{"type":"boolean","description":"Whether another page exists."}},"required":["nextCursor","hasMore"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Create jobs from services

> Creates one pending job per service for a customer, priced as given, exactly as adding jobs to a customer in the app does. Jobs have no visits until you schedule them with \`POST /v1/jobs/{id}/schedule\`. When the workspace has a single crew, new jobs get it. A service's \`externalReference\` saves your id for its job in the same request, and a duplicate fails the whole request with 409. Send an \`Idempotency-Key\` to retry safely.\
> \
> Returns a list of \[job objects]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`already\_exists\`]\(<https://docs.shortcutco.com/developers/concepts/errors#already\\_exists>), \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs":{"post":{"operationId":"jobs.create","tags":["Jobs"],"summary":"Create jobs from services","description":"Creates one pending job per service for a customer, priced as given, exactly as adding jobs to a customer in the app does. Jobs have no visits until you schedule them with `POST /v1/jobs/{id}/schedule`. When the workspace has a single crew, new jobs get it. A service's `externalReference` saves your id for its job in the same request, and a duplicate fails the whole request with 409. Send an `Idempotency-Key` to retry safely.\n\nReturns a list of [job objects](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`already_exists`](https://docs.shortcutco.com/developers/concepts/errors#already_exists), [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string","format":"uuid","description":"The customer the jobs are for."},"propertyId":{"type":"string","format":"uuid","description":"The customer's property to service. Required when the customer has more than one active property."},"services":{"type":"array","items":{"type":"object","properties":{"serviceId":{"type":"string","format":"uuid","description":"The catalog service to sell. Each service may appear once per request."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"`one_off` for a single visit, `recurring` for a repeating service. Snow removal jobs are created in the app."},"cadenceFrequency":{"type":"integer","minimum":1,"maximum":366,"description":"Recurring jobs only: how many visits per `cadencePeriod`, such as 1 for weekly with `per_week`."},"cadencePeriod":{"type":"string","enum":["per_week","per_month","per_year","per_day"],"description":"Recurring jobs only: the period `cadenceFrequency` counts in."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced. Defaults to the service's price structure."},"quantity":{"type":"number","minimum":0,"maximum":100000000,"default":1,"description":"The units priced (hours, square feet, or 1 for a flat price). Must be above 0 except for `per_man_hour` jobs. Defaults to 1."},"rate":{"type":"number","minimum":0,"maximum":10000000,"description":"The price per unit, in US dollars."},"externalReference":{"type":"object","properties":{"source":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$","description":"Your name for the system the id comes from, such as `hubspot` or `mywebsite`: 1-64 lowercase letters, digits, `_`, `.`, or `-`, starting with a letter or digit."},"externalId":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[^\\p{Cc}\\s/\\\\]+$","description":"The object's id in that system: 1-255 characters, no spaces, slashes, or control characters."}},"required":["source","externalId"],"additionalProperties":false,"description":"Your id for the job this service creates. It is saved with the job in the same request; if another job already has it, nothing is created and the response is 409 `already_exists`."}},"required":["serviceId","cadence","rate"],"additionalProperties":false},"minItems":1,"maxItems":25,"description":"One job is created per service. Jobs created together are grouped, so their visits share a stop."}},"required":["customerId","services"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Job"}},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Get a job

> One job with its price, cadence, status, crew, and next visit. \`teamId\` is the job's own crew; \`effectiveTeamId\` is the crew doing its next visit.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}":{"get":{"operationId":"jobs.get","tags":["Jobs"],"summary":"Get a job","description":"One job with its price, cadence, status, crew, and next visit. `teamId` is the job's own crew; `effectiveTeamId` is the crew doing its next visit.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Update a job

> Changes a job's name, description, or crew. Only the fields you send change. Changing the crew works like changing a recurring job's crew in the app: upcoming open visits that follow the job's crew move with it, visits pinned to the previous crew are unpinned, and when the job had no crew every pinned upcoming visit is unpinned (see \`teamId\`). Prices are not editable here.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}":{"patch":{"operationId":"jobs.update","tags":["Jobs"],"summary":"Update a job","description":"Changes a job's name, description, or crew. Only the fields you send change. Changing the crew works like changing a recurring job's crew in the app: upcoming open visits that follow the job's crew move with it, visits pinned to the previous crew are unpinned, and when the job had no crew every pinned upcoming visit is unpinned (see `teamId`). Prices are not editable here.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"A new name for the job."},"description":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"description":"A new description, or null to clear it."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Recurring jobs only: the crew that owns the job, or null for none. Only upcoming open visits (pending, scheduled, or rescheduled, dated today or later) change. If the job had a crew, visits pinned to that previous crew are unpinned and follow the job's crew; visits pinned to any other crew, including the new one, keep their pin. If the job had no crew, every pinned upcoming open visit is unpinned and follows the new crew. Setting the job's current crew again unpins visits pinned to it. With null, visits pinned to the previous crew are left with no crew. Finished and past visits never change."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Cancel a job

> Cancels a job and its open visits (pending, scheduled, and rescheduled), exactly as canceling it in the app does. Completed visits keep their history. Canceling a canceled job changes nothing; a completed job cannot be canceled. A job with man-hour work still open cannot be canceled until that work is finished. This cannot be undone.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/cancel":{"post":{"operationId":"jobs.cancel","tags":["Jobs"],"summary":"Cancel a job","description":"Cancels a job and its open visits (pending, scheduled, and rescheduled), exactly as canceling it in the app does. Completed visits keep their history. Canceling a canceled job changes nothing; a completed job cannot be canceled. A job with man-hour work still open cannot be canceled until that work is finished. This cannot be undone.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Link a job to your id

> Records \`externalId\` as this job's id in \`source\`, so the external lookup and upsert find it. Linking is always explicit; the API never matches a job to your id by itself. Repeating the same link changes nothing. If the id already belongs to another job, or this job already has a different id in \`source\`, the request fails with 409 and nothing changes. A job a price change replaced is 409 \`job\_superseded\`, naming the current version; link that one. When the app replaces a job later, its ids move to the new version.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`already\_exists\`]\(<https://docs.shortcutco.com/developers/concepts/errors#already\\_exists>), \[\`external\_reference\_exists\`]\(<https://docs.shortcutco.com/developers/concepts/errors#external\\_reference\\_exists>), \[\`job\_superseded\`]\(<https://docs.shortcutco.com/developers/concepts/errors#job\\_superseded>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/external-references/{source}":{"put":{"operationId":"jobs.externalReferences.link","tags":["Jobs"],"summary":"Link a job to your id","description":"Records `externalId` as this job's id in `source`, so the external lookup and upsert find it. Linking is always explicit; the API never matches a job to your id by itself. Repeating the same link changes nothing. If the id already belongs to another job, or this job already has a different id in `source`, the request fails with 409 and nothing changes. A job a price change replaced is 409 `job_superseded`, naming the current version; link that one. When the app replaces a job later, its ids move to the new version.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`already_exists`](https://docs.shortcutco.com/developers/concepts/errors#already_exists), [`external_reference_exists`](https://docs.shortcutco.com/developers/concepts/errors#external_reference_exists), [`job_superseded`](https://docs.shortcutco.com/developers/concepts/errors#job_superseded). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"name":"source","in":"path","required":true,"description":"Your name for the system the id comes from, such as `hubspot` or `mywebsite`: 1-64 lowercase letters, digits, `_`, `.`, or `-`, starting with a letter or digit.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"externalId":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[^\\p{Cc}\\s/\\\\]+$","description":"The object's id in that system: 1-255 characters, no spaces, slashes, or control characters."}},"required":["externalId"],"additionalProperties":false,"description":"The object's id in that system."}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Unlink a job from your id

> Removes this job's id in \`source\`. The job itself is unchanged. Unlinking when there is no link changes nothing.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/external-references/{source}":{"delete":{"operationId":"jobs.externalReferences.unlink","tags":["Jobs"],"summary":"Unlink a job from your id","description":"Removes this job's id in `source`. The job itself is unchanged. Unlinking when there is no link changes nothing.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"name":"source","in":"path","required":true,"description":"Your name for the system the id comes from, such as `hubspot` or `mywebsite`: 1-64 lowercase letters, digits, `_`, `.`, or `-`, starting with a letter or digit.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Pause a job

> Puts a scheduled job on hold, indefinitely or until \`pausedUntil\`, exactly as pausing it in the app does; a note is added to the customer. Pending, canceled, and completed jobs cannot be paused. Pausing a job again with the same values changes nothing. Send \`{}\` to pause indefinitely with no reason.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/pause":{"post":{"operationId":"jobs.pause","tags":["Jobs"],"summary":"Pause a job","description":"Puts a scheduled job on hold, indefinitely or until `pausedUntil`, exactly as pausing it in the app does; a note is added to the customer. Pending, canceled, and completed jobs cannot be paused. Pausing a job again with the same values changes nothing. Send `{}` to pause indefinitely with no reason.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pausedUntil":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Pause until this date, `YYYY-MM-DD` in the workspace's timezone; today or later. Omit it to pause indefinitely."},"reason":{"type":"string","maxLength":500,"description":"Why the job is paused, shown in the app."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Resume a job

> Takes a paused job off hold, exactly as resuming it in the app does: its open visits are canceled and its visits are planned again from \`resumeDate\` (today when omitted); a note is added to the customer. Resuming a job that is already scheduled changes nothing. Pending jobs are scheduled instead, and canceled and completed jobs cannot be resumed. Send \`{}\` to resume today.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/resume":{"post":{"operationId":"jobs.resume","tags":["Jobs"],"summary":"Resume a job","description":"Takes a paused job off hold, exactly as resuming it in the app does: its open visits are canceled and its visits are planned again from `resumeDate` (today when omitted); a note is added to the customer. Resuming a job that is already scheduled changes nothing. Pending jobs are scheduled instead, and canceled and completed jobs cannot be resumed. Send `{}` to resume today.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"resumeDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The date the job's visits start again, `YYYY-MM-DD` in the workspace's timezone; today or later. Omit it to resume today."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Schedule a job

> Puts a pending job on the schedule, exactly as scheduling it in the app does: its visits are created from \`startDate\` (one for a one-time job, a series for a recurring one) and appended to the crew's route for each day. Only pending jobs can be scheduled; move existing visits with the visit endpoints instead. Your job-scheduled automations, which can email or text the customer, run only if the token has \`automations:trigger\`. Send an \`Idempotency-Key\` to retry safely.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/schedule":{"post":{"operationId":"jobs.schedule","tags":["Jobs"],"summary":"Schedule a job","description":"Puts a pending job on the schedule, exactly as scheduling it in the app does: its visits are created from `startDate` (one for a one-time job, a series for a recurring one) and appended to the crew's route for each day. Only pending jobs can be scheduled; move existing visits with the visit endpoints instead. Your job-scheduled automations, which can email or text the customer, run only if the token has `automations:trigger`. Send an `Idempotency-Key` to retry safely.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"id","in":"path","required":true,"description":"The object's id.","schema":{"type":"string","format":"uuid"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"startDate":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The first visit's date, `YYYY-MM-DD` in the workspace's timezone; today or later. A recurring job's later visits follow its cadence from here."},"teamId":{"type":"string","format":"uuid","description":"The crew for the visits this creates. Omit it to use the job's crew. For a recurring job with no crew yet, this crew also becomes the job's crew. Visits given the job's own crew store none of their own and follow the job's crew."}},"required":["startDate"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## List a job's visits

> The job's visits newest first, or by last change when \`updatedSince\` is set: the same visits, and the same shape, as \`GET /v1/visits?jobId=\`. Needs \`visits:read\` as well as \`jobs:read\`.\
> \
> Returns a list of \[visit objects]\(<https://docs.shortcutco.com/developers/reference/objects/visit>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:read","visits:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Visit":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The visit's id."},"object":{"type":"string","const":"visit","description":"Always `visit`."},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"The scheduled date, `YYYY-MM-DD` in the workspace's timezone."},"status":{"type":"string","enum":["pending","scheduled","completed","canceled","rescheduled","needs_reschedule","skipped"],"description":"`scheduled` visits are on the schedule, and `rescheduled` visits were moved to this date and are on the schedule too. `pending` visits are planned but not on the schedule yet. `completed` visits are done. `needs_reschedule` visits are waiting for a new date. `skipped` and `canceled` visits are closed."},"closeReason":{"anyOf":[{"type":"string","enum":["already_handled"]},{"type":"null"}],"description":"`already_handled` when a canceled visit was closed because the work was handled outside Shortcut (the app shows it as Handled); otherwise null."},"jobId":{"type":"string","format":"uuid","description":"The job this visit belongs to."},"jobName":{"type":"string","description":"The job's name, such as `Weekly mowing`."},"customerId":{"type":"string","format":"uuid","description":"The customer the visit is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"assignedTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew pinned to this visit. Null means the visit inherits its job's crew; read `effectiveTeamId` for the crew doing the work."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the visit: `assignedTeamId` when set, otherwise the job's crew. Null when neither has a crew (unassigned)."},"routePosition":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"The visit's stop number on its crew's route for the day, starting at 1. Null when the visit is not routed or has no crew."},"plannedStartAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Planned start, ISO 8601 in UTC, or null."},"plannedEndAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Planned end, ISO 8601 in UTC, or null."},"estimatedDurationMinutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Estimated time on site in minutes, when known."},"arrivedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the crew arrived on site (read-only), ISO 8601 in UTC, or null."},"completedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the visit was completed, ISO 8601 in UTC, or null."},"invoiceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The invoice that bills this visit, once a completed visit has been billed; otherwise null."},"createdAt":{"type":"string","format":"date-time","description":"When the visit was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the visit last changed, ISO 8601 in UTC."}},"required":["id","object","date","status","closeReason","jobId","jobName","customerId","propertyId","address","assignedTeamId","effectiveTeamId","routePosition","plannedStartAt","plannedEndAt","estimatedDurationMinutes","arrivedAt","completedAt","invoiceId","createdAt","updatedAt"],"additionalProperties":false,"description":"A visit: one scheduled service for a customer on one date."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/{id}/visits":{"get":{"operationId":"jobs.visits.list","tags":["Jobs"],"summary":"List a job's visits","description":"The job's visits newest first, or by last change when `updatedSince` is set: the same visits, and the same shape, as `GET /v1/visits?jobId=`. Needs `visits:read` as well as `jobs:read`.\n\nReturns a list of [visit objects](https://docs.shortcutco.com/developers/reference/objects/visit).","parameters":[{"name":"id","in":"path","required":true,"description":"The job's id.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","required":false,"description":"How many items to return, 1-200. Defaults to 50.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"The `meta.nextCursor` from the previous page. Omit it for the first page.","schema":{"type":"string","minLength":1,"maxLength":4096}},{"name":"status","in":"query","required":false,"description":"Only visits with this status.","schema":{"type":"string","enum":["pending","scheduled","completed","canceled","rescheduled","needs_reschedule","skipped"]}},{"name":"updatedSince","in":"query","required":false,"description":"ISO 8601 timestamp. Returns objects changed at or after it, oldest change first. Keep the largest `updatedAt` you have seen, subtract five minutes, and pass it on the next sync; deduplicate by `id`.","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Visit"}},"meta":{"type":"object","properties":{"nextCursor":{"type":["string","null"],"description":"Pass as `cursor` to get the next page; null on the last page."},"hasMore":{"type":"boolean","description":"Whether another page exists."}},"required":["nextCursor","hasMore"],"additionalProperties":false}},"required":["data","meta"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Get a job by your id

> The job your id maps to, as \`GET /v1/jobs/{id}\` returns it, or 404. A price change in the app moves your id to the job's new version; an id that still names a replaced version is 409 \`job\_superseded\`, naming the current job.\
> \
> Returns \[the job object]\(<https://docs.shortcutco.com/developers/reference/objects/job).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`job\_superseded\`]\(<https://docs.shortcutco.com/developers/concepts/errors#job\\_superseded>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:read"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/external/{source}/{externalId}":{"get":{"operationId":"jobs.external.get","tags":["Jobs"],"summary":"Get a job by your id","description":"The job your id maps to, as `GET /v1/jobs/{id}` returns it, or 404. A price change in the app moves your id to the job's new version; an id that still names a replaced version is 409 `job_superseded`, naming the current job.\n\nReturns [the job object](https://docs.shortcutco.com/developers/reference/objects/job).\n\n**Errors specific to this endpoint:** [`job_superseded`](https://docs.shortcutco.com/developers/concepts/errors#job_superseded). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"source","in":"path","required":true,"description":"Your name for the system the id comes from, such as `hubspot` or `mywebsite`: 1-64 lowercase letters, digits, `_`, `.`, or `-`, starting with a letter or digit.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$"}},{"name":"externalId","in":"path","required":true,"description":"The object's id in that system: 1-255 characters, no spaces, slashes, or control characters.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[^\\p{Cc}\\s/\\\\]+$"}}],"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Job"},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```

## Create or update a job by your id

> The sync endpoint. If your id is new, creates one pending job as a service of \`POST /v1/jobs\` does, applies \`name\`, \`description\`, and \`teamId\` when sent, and saves the id (\`created: true\`, status 201). If it exists, applies the \`name\`, \`description\`, and \`teamId\` that differ from the stored ones as \`PATCH /v1/jobs/{id}\` does and ignores the property, service, cadence, and price (\`created: false\`, status 200); a request that changes nothing writes nothing, and re-sending the job's current crew leaves its visits alone. \`customerId\` must be the mapped job's customer, or the request fails with 409. Concurrent requests for the same id run one after another, so they never create two jobs. When a price change in the app replaces a job with a new version, your id moves to the new version; an id that still names the replaced version is 409 \`job\_superseded\`, naming the current job.\
> \
> \*\*Errors specific to this endpoint:\*\* \[\`external\_reference\_changed\`]\(<https://docs.shortcutco.com/developers/concepts/errors#external\\_reference\\_changed>), \[\`external\_reference\_customer\_mismatch\`]\(<https://docs.shortcutco.com/developers/concepts/errors#external\\_reference\\_customer\\_mismatch>), \[\`job\_superseded\`]\(<https://docs.shortcutco.com/developers/concepts/errors#job\\_superseded>), \[\`rule\_violation\`]\(<https://docs.shortcutco.com/developers/concepts/errors#rule\\_violation>), \[\`state\_conflict\`]\(<https://docs.shortcutco.com/developers/concepts/errors#state\\_conflict>). Every endpoint can also return the \[common errors]\(<https://docs.shortcutco.com/developers/concepts/errors>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Jobs","description":"A job is work sold to a customer at a property, made from a catalog service. Scheduling a job puts its visits on the schedule.\n\nFields: [The job object](https://docs.shortcutco.com/developers/reference/objects/job)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["jobs:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"scut_<env>_<id>_<secret>","description":"A workspace API token. Each operation lists the scopes it needs; `write` implies `read`, and `send`/`complete` are never implied."}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Any string up to 255 characters. Retrying with the same key returns the first response instead of repeating the write, for 24 hours.","schema":{"type":"string","maxLength":255}}},"headers":{"RateLimitLimit":{"schema":{"type":"integer"},"description":"Requests allowed in the tightest window."},"RateLimitRemaining":{"schema":{"type":"integer"},"description":"Requests left in that window."},"RateLimitReset":{"schema":{"type":"integer"},"description":"Seconds until that window resets."}},"schemas":{"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The job's id."},"object":{"type":"string","const":"job","description":"Always `job`."},"name":{"type":"string","description":"The job's name, such as `Weekly mowing`. New jobs take their service's name."},"description":{"type":["string","null"],"description":"The job's description, when it has one."},"status":{"type":"string","enum":["pending","scheduled","paused","canceled","completed"],"description":"`pending` jobs are not scheduled yet (schedule them with `POST /v1/jobs/{id}/schedule`). `scheduled` jobs have visits on the schedule. `paused` jobs are on hold. `canceled` and `completed` jobs are closed."},"customerId":{"type":"string","format":"uuid","description":"The customer the job is for."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property (service address), when the job has one."},"address":{"type":["string","null"],"description":"The property's street address, when the job has a property."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the job was created from, when it still exists."},"estimateId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The estimate the job came from, when it was created by approving one."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`one_off` jobs have one visit, `recurring` jobs repeat, `on_demand` jobs (snow removal) get a visit per storm."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"For recurring jobs, how many visits per `cadencePeriod`; null otherwise."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"For recurring jobs, the period `cadenceFrequency` counts in; null otherwise."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced: `flat`, `per_hour`, `per_sqft`, or `per_man_hour`."},"rate":{"type":"number","description":"The price per unit, in US dollars."},"quantity":{"type":["number","null"],"description":"The units priced (hours, square feet, or 1 for a flat price)."},"subtotal":{"type":"number","description":"The price per visit, in US dollars (`rate` × `quantity`)."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's own crew (for a recurring job, its owner). Visits with no crew of their own inherit it. Null when the job has no crew."},"effectiveTeamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The crew doing the job's next visit: that visit's own crew when it has one, otherwise `teamId`. Equals `teamId` when the job has no upcoming visit."},"nextVisitId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The job's next visit on or after today, when it has one."},"nextVisitDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"The next visit's date, `YYYY-MM-DD` in the workspace's timezone, or null."},"pausedUntil":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"For a job paused until a date, that date (`YYYY-MM-DD`); null when it is paused indefinitely or not paused."},"pauseReason":{"type":["string","null"],"description":"Why the job was paused, when a reason was given."},"previousVersionJobId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"When a price change replaced an earlier job with this one, the earlier job's id."},"effectiveUntilDate":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"description":"When a price change replaced this job with a newer one, the last date this job covers (`YYYY-MM-DD`); null for the current job."},"externalReferences":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReference"},"description":"Ids this job has in your other systems, set with `externalReference`, the external upsert, or a link."},"createdAt":{"type":"string","format":"date-time","description":"When the job was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the job last changed, ISO 8601 in UTC."}},"required":["id","object","name","description","status","customerId","propertyId","address","serviceId","estimateId","cadence","cadenceFrequency","cadencePeriod","priceStructure","rate","quantity","subtotal","teamId","effectiveTeamId","nextVisitId","nextVisitDate","pausedUntil","pauseReason","previousVersionJobId","effectiveUntilDate","externalReferences","createdAt","updatedAt"],"additionalProperties":false,"description":"A job: a service a customer buys, one-time or recurring, and the source of its visits."},"ExternalReference":{"type":"object","properties":{"source":{"type":"string","description":"The system the id comes from."},"externalId":{"type":"string","description":"The object's id in that system."}},"required":["source","externalId"],"additionalProperties":false,"description":"An id this object has in another system."},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error family, such as `invalid_request` or `permission`."},"code":{"type":"string","description":"Specific error code; see the errors page."},"message":{"type":"string","description":"A human-readable explanation."},"param":{"type":"string","description":"The input the error is about, such as `query.limit`."},"requiredScope":{"type":"string","description":"The scope the token is missing."},"requestId":{"type":"string","description":"Quote this id to support."},"docs":{"type":"string","description":"Link to the error's documentation."}},"required":["type","code","message","requestId","docs"],"additionalProperties":false}},"required":["error"],"additionalProperties":false}},"responses":{"BadRequest":{"description":"The request is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthenticated":{"description":"The token is missing, invalid, expired, or revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"SubscriptionRequired":{"description":"The workspace's subscription needs attention.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token is missing a required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such object in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflict with the current state or an in-flight request with the same idempotency key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request cannot be applied, or its idempotency key was used for a different request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Rate limited; see Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"Temporarily unavailable; retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/jobs/external/{source}/{externalId}":{"put":{"operationId":"jobs.external.upsert","tags":["Jobs"],"summary":"Create or update a job by your id","description":"The sync endpoint. If your id is new, creates one pending job as a service of `POST /v1/jobs` does, applies `name`, `description`, and `teamId` when sent, and saves the id (`created: true`, status 201). If it exists, applies the `name`, `description`, and `teamId` that differ from the stored ones as `PATCH /v1/jobs/{id}` does and ignores the property, service, cadence, and price (`created: false`, status 200); a request that changes nothing writes nothing, and re-sending the job's current crew leaves its visits alone. `customerId` must be the mapped job's customer, or the request fails with 409. Concurrent requests for the same id run one after another, so they never create two jobs. When a price change in the app replaces a job with a new version, your id moves to the new version; an id that still names the replaced version is 409 `job_superseded`, naming the current job.\n\n**Errors specific to this endpoint:** [`external_reference_changed`](https://docs.shortcutco.com/developers/concepts/errors#external_reference_changed), [`external_reference_customer_mismatch`](https://docs.shortcutco.com/developers/concepts/errors#external_reference_customer_mismatch), [`job_superseded`](https://docs.shortcutco.com/developers/concepts/errors#job_superseded), [`rule_violation`](https://docs.shortcutco.com/developers/concepts/errors#rule_violation), [`state_conflict`](https://docs.shortcutco.com/developers/concepts/errors#state_conflict). Every endpoint can also return the [common errors](https://docs.shortcutco.com/developers/concepts/errors).","parameters":[{"name":"source","in":"path","required":true,"description":"Your name for the system the id comes from, such as `hubspot` or `mywebsite`: 1-64 lowercase letters, digits, `_`, `.`, or `-`, starting with a letter or digit.","schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_.-]{0,63}$"}},{"name":"externalId","in":"path","required":true,"description":"The object's id in that system: 1-255 characters, no spaces, slashes, or control characters.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[^\\p{Cc}\\s/\\\\]+$"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string","format":"uuid","description":"The customer the job is for. When the reference exists, it must be the mapped job's customer."},"propertyId":{"type":"string","format":"uuid","description":"The customer's property to service. Required when the customer has more than one active property."},"serviceId":{"type":"string","format":"uuid","description":"The catalog service to sell. Each service may appear once per request."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"`one_off` for a single visit, `recurring` for a repeating service. Snow removal jobs are created in the app."},"cadenceFrequency":{"type":"integer","minimum":1,"maximum":366,"description":"Recurring jobs only: how many visits per `cadencePeriod`, such as 1 for weekly with `per_week`."},"cadencePeriod":{"type":"string","enum":["per_week","per_month","per_year","per_day"],"description":"Recurring jobs only: the period `cadenceFrequency` counts in."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How the job is priced. Defaults to the service's price structure."},"quantity":{"type":"number","minimum":0,"maximum":100000000,"default":1,"description":"The units priced (hours, square feet, or 1 for a flat price). Must be above 0 except for `per_man_hour` jobs. Defaults to 1."},"rate":{"type":"number","minimum":0,"maximum":10000000,"description":"The price per unit, in US dollars."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"A new name for the job."},"description":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}],"description":"A new description, or null to clear it."},"teamId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Recurring jobs only: the crew that owns the job, or null for none. Applied when it differs from the job's crew, with the same effect on upcoming visits as `PATCH /v1/jobs/{id}`; sending the job's current crew again changes nothing and leaves pinned visits pinned. A one-time job ignores null."}},"required":["customerId","serviceId","cadence","rate"],"additionalProperties":false,"description":"One job as a service of `POST /v1/jobs` creates it, with an optional name, description, and crew. When the reference is new the job is created; when it exists, `name`, `description`, and `teamId` that differ update the mapped job, and the property, service, cadence, and price are ignored."}}}},"responses":{"200":{"description":"Success.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"created":{"type":"boolean","description":"True when this request created the job."},"job":{"$ref":"#/components/schemas/Job"}},"required":["created","job"],"additionalProperties":false},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"201":{"description":"Your id was new: the job was created and your id saved (`created: true`).","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"created":{"type":"boolean","description":"True when this request created the job."},"job":{"$ref":"#/components/schemas/Job"}},"required":["created","job"],"additionalProperties":false},"meta":{"type":"object","properties":{},"additionalProperties":true,"description":"Facts about this request beside the object, such as `alreadyReminded` on a repeated reminder. Present only when there is something to report."}},"required":["data"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"402":{"$ref":"#/components/responses/SubscriptionRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/InternalError"},"503":{"$ref":"#/components/responses/Unavailable"}}}}}}
```
