> 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/estimates.md).

# Estimates

Prices offered to a lead or customer.

An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.

Fields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder).

## List estimates

> Estimates newest first, or by last change when \`updatedSince\` is set, without their lines; get one estimate for its lines.\
> \
> Returns a list of \[estimate objects]\(<https://docs.shortcutco.com/developers/reference/objects/estimate>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"Estimate":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt"],"additionalProperties":false,"description":"An estimate: prices offered to a lead or customer."},"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/estimates":{"get":{"operationId":"estimates.list","tags":["Estimates"],"summary":"List estimates","description":"Estimates newest first, or by last change when `updatedSince` is set, without their lines; get one estimate for its lines.\n\nReturns a list of [estimate objects](https://docs.shortcutco.com/developers/reference/objects/estimate).","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":"status","in":"query","required":false,"description":"Only estimates with this status.","schema":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"]}},{"name":"customerId","in":"query","required":false,"description":"Only this customer's estimates.","schema":{"type":"string","format":"uuid"}},{"name":"leadId","in":"query","required":false,"description":"Only estimates for this lead.","schema":{"type":"string","format":"uuid"}},{"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/Estimate"}},"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 an estimate

> Creates a draft estimate for a lead, as New estimate does in the app. Lines reference catalog services by id; the app takes each line's name and price structure from the catalog and computes subtotals and the total. Unit costs come from the catalog and are never accepted or returned. If the lead's email belongs to an existing customer, the estimate links to them, which needs \`customers:write\`. The estimate is not sent until you send it; workspace automations on new estimates may react if the token has \`automations:trigger\`. Send an \`Idempotency-Key\` to retry safely.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`service\_not\_supported\`]\(<https://docs.shortcutco.com/developers/concepts/errors#service\\_not\\_supported>). 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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates":{"post":{"operationId":"estimates.create","tags":["Estimates"],"summary":"Create an estimate","description":"Creates a draft estimate for a lead, as New estimate does in the app. Lines reference catalog services by id; the app takes each line's name and price structure from the catalog and computes subtotals and the total. Unit costs come from the catalog and are never accepted or returned. If the lead's email belongs to an existing customer, the estimate links to them, which needs `customers:write`. The estimate is not sent until you send it; workspace automations on new estimates may react if the token has `automations:trigger`. Send an `Idempotency-Key` to retry safely.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).\n\n**Errors specific to this endpoint:** [`service_not_supported`](https://docs.shortcutco.com/developers/concepts/errors#service_not_supported). 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":{"leadId":{"type":"string","format":"uuid","description":"The lead the estimate answers. Its customer account and property are used; if the lead's email belongs to an existing customer it is linked to them, which needs `customers:write`."},"propertyId":{"type":"string","format":"uuid","description":"One of the customer's properties. Defaults to the lead's property."},"message":{"type":"string","maxLength":4000,"description":"A message to the customer, sent with the estimate."},"lines":{"type":"array","items":{"type":"object","properties":{"serviceId":{"type":"string","format":"uuid","description":"The catalog service the line sells. Its name and price structure come from the catalog."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"`recurring` for repeat work, `one_off` for a single visit."},"cadenceFrequency":{"type":"integer","minimum":1,"maximum":31,"description":"For recurring lines, visits per `cadencePeriod`, such as `1` with `per_week`."},"cadencePeriod":{"type":"string","enum":["per_day","per_week","per_month","per_year"],"description":"For recurring lines, the period `cadenceFrequency` counts in."},"rate":{"type":"number","minimum":0,"maximum":1000000,"description":"The price in US dollars, per the service's price structure. Defaults to the service's rate."},"quantity":{"type":"number","minimum":0,"maximum":100000000,"description":"What `rate` multiplies: square feet for per-square-foot services, hours for hourly ones."},"description":{"type":"string","maxLength":2000,"description":"Shown under the line. Defaults to the service's description."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"choiceGroupId":{"type":"string","format":"uuid","description":"Give lines the same new UUID to offer them as alternatives; the customer picks one, the first by default."},"options":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":140,"description":"The option's name, such as `Biweekly`."},"rate":{"type":"number","minimum":0,"maximum":1000000,"description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"How often the option's work happens."}},"required":["name","rate","priceStructure","cadence"],"additionalProperties":false},"maxItems":10,"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid","description":"A product from the catalog."},"quantity":{"type":"number","minimum":0,"maximum":1000000,"description":"How many units."},"unitPrice":{"type":"number","minimum":0,"maximum":1000000,"description":"The customer's price per unit in US dollars. Defaults to the product's price."},"taxable":{"type":"boolean","description":"Whether sales tax applies. Defaults to the product's setting."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line; `included` folds it into the service's price."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"Overrides the product's description."}},"required":["productId","quantity"],"additionalProperties":false},"maxItems":100,"description":"Products sold with the line."}},"required":["serviceId","cadence"],"additionalProperties":false},"maxItems":100,"default":[],"description":"The estimate's lines, in order."}},"required":["leadId"],"additionalProperties":false,"description":"A new draft estimate."}}}},"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":{"$ref":"#/components/schemas/EstimateWithLines"},"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 an estimate

> One estimate with its lines, their price options, and the products sold with them.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate>).

```json
{"openapi":"3.1.0","info":{"title":"Shortcut API","version":"v1"},"tags":[{"name":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates/{id}":{"get":{"operationId":"estimates.get","tags":["Estimates"],"summary":"Get an estimate","description":"One estimate with its lines, their price options, and the products sold with them.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).","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/EstimateWithLines"},"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 draft estimate

> Changes a draft estimate's message, property, or lines, as editing it in the app does. \`lines\` replaces every line; omit it to keep them. Only drafts can change: a sent, approved, or closed estimate is \`409 state\_conflict\`. The app recomputes the total.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`service\_not\_supported\`]\(<https://docs.shortcutco.com/developers/concepts/errors#service\\_not\\_supported>), \[\`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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates/{id}":{"patch":{"operationId":"estimates.update","tags":["Estimates"],"summary":"Update a draft estimate","description":"Changes a draft estimate's message, property, or lines, as editing it in the app does. `lines` replaces every line; omit it to keep them. Only drafts can change: a sent, approved, or closed estimate is `409 state_conflict`. The app recomputes the total.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).\n\n**Errors specific to this endpoint:** [`service_not_supported`](https://docs.shortcutco.com/developers/concepts/errors#service_not_supported), [`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"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"propertyId":{"type":"string","format":"uuid","description":"Another of the customer's properties."},"message":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"description":"The message to the customer; `null` clears it."},"lines":{"type":"array","items":{"type":"object","properties":{"serviceId":{"type":"string","format":"uuid","description":"The catalog service the line sells. Its name and price structure come from the catalog."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"`recurring` for repeat work, `one_off` for a single visit."},"cadenceFrequency":{"type":"integer","minimum":1,"maximum":31,"description":"For recurring lines, visits per `cadencePeriod`, such as `1` with `per_week`."},"cadencePeriod":{"type":"string","enum":["per_day","per_week","per_month","per_year"],"description":"For recurring lines, the period `cadenceFrequency` counts in."},"rate":{"type":"number","minimum":0,"maximum":1000000,"description":"The price in US dollars, per the service's price structure. Defaults to the service's rate."},"quantity":{"type":"number","minimum":0,"maximum":100000000,"description":"What `rate` multiplies: square feet for per-square-foot services, hours for hourly ones."},"description":{"type":"string","maxLength":2000,"description":"Shown under the line. Defaults to the service's description."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"choiceGroupId":{"type":"string","format":"uuid","description":"Give lines the same new UUID to offer them as alternatives; the customer picks one, the first by default."},"options":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":140,"description":"The option's name, such as `Biweekly`."},"rate":{"type":"number","minimum":0,"maximum":1000000,"description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring"],"description":"How often the option's work happens."}},"required":["name","rate","priceStructure","cadence"],"additionalProperties":false},"maxItems":10,"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string","format":"uuid","description":"A product from the catalog."},"quantity":{"type":"number","minimum":0,"maximum":1000000,"description":"How many units."},"unitPrice":{"type":"number","minimum":0,"maximum":1000000,"description":"The customer's price per unit in US dollars. Defaults to the product's price."},"taxable":{"type":"boolean","description":"Whether sales tax applies. Defaults to the product's setting."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line; `included` folds it into the service's price."},"description":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"Overrides the product's description."}},"required":["productId","quantity"],"additionalProperties":false},"maxItems":100,"description":"Products sold with the line."}},"required":["serviceId","cadence"],"additionalProperties":false},"maxItems":100,"description":"Replaces every line. Omit to keep the lines as they are."}},"additionalProperties":false,"description":"Changes to a draft estimate."}}}},"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/EstimateWithLines"},"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"}}}}}}
```

## Approve an estimate

> Approves an estimate on the customer's behalf, exactly as Approve does in the app: it creates the estimate's jobs (pending, ready to schedule), queues their visits for recurring work, and emails your admins. A draft can be approved directly. Approving an approved estimate creates nothing and returns it with \`meta.alreadyApproved: true\`. An estimate past its expiry is marked expired and refused with \`409 estimate\_expired\`. If the lead's email belongs to an existing customer the estimate links to them, which needs \`customers:write\`. Your estimate-accepted automations, and workspace automations on approved estimates and on the visits it schedules, run only if the token has \`automations:trigger\`. If the follow-up work cannot be queued the response is \`503\`; approve again to queue it.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`estimate\_expired\`]\(<https://docs.shortcutco.com/developers/concepts/errors#estimate\\_expired>), \[\`estimate\_not\_approvable\`]\(<https://docs.shortcutco.com/developers/concepts/errors#estimate\\_not\\_approvable>), \[\`follow\_up\_not\_queued\`]\(<https://docs.shortcutco.com/developers/concepts/errors#follow\\_up\\_not\\_queued>), \[\`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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates/{id}/approve":{"post":{"operationId":"estimates.approve","tags":["Estimates"],"summary":"Approve an estimate","description":"Approves an estimate on the customer's behalf, exactly as Approve does in the app: it creates the estimate's jobs (pending, ready to schedule), queues their visits for recurring work, and emails your admins. A draft can be approved directly. Approving an approved estimate creates nothing and returns it with `meta.alreadyApproved: true`. An estimate past its expiry is marked expired and refused with `409 estimate_expired`. If the lead's email belongs to an existing customer the estimate links to them, which needs `customers:write`. Your estimate-accepted automations, and workspace automations on approved estimates and on the visits it schedules, run only if the token has `automations:trigger`. If the follow-up work cannot be queued the response is `503`; approve again to queue it.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).\n\n**Errors specific to this endpoint:** [`estimate_expired`](https://docs.shortcutco.com/developers/concepts/errors#estimate_expired), [`estimate_not_approvable`](https://docs.shortcutco.com/developers/concepts/errors#estimate_not_approvable), [`follow_up_not_queued`](https://docs.shortcutco.com/developers/concepts/errors#follow_up_not_queued), [`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":{"selections":{"type":"array","items":{"type":"object","properties":{"lineId":{"type":"string","format":"uuid","description":"A line of the estimate."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose."},"removed":{"type":"boolean","description":"`true` when the customer left this optional line out; `false` keeps a line they had left out on the estimate page. Omit it to leave the line as it is."}},"required":["lineId"],"additionalProperties":false},"maxItems":100,"default":[],"description":"The customer's choices, as they would make them on the estimate page."}},"additionalProperties":false,"description":"The customer's choices when approving."}}}},"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/EstimateWithLines"},"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"}}}}}}
```

## Remind about an estimate

> Reminds the customer about a pending estimate by email, text, or both, as Remind does in the app. Without \`sendEmail\` or \`sendSms\` the reminder uses the channels of the latest send. Suppressed and complaint-suppressed addresses and customers who opted out of texts are skipped, and \`skippedChannels\` says why. At most one reminder per estimate in ten minutes (\`429 estimate\_recently\_reminded\`). Repeating a request with the same \`Idempotency-Key\` returns the first reminder with \`meta.alreadyReminded: true\` once it was sent, however late. While the first attempt is still sending, the repeat gets a retryable \`409 reminder\_in\_progress\` with \`Retry-After\`; if that attempt stopped before finishing, a repeat five minutes after it started sends the reminder, so a reminder interrupted mid-send can reach the customer twice. Needs \`estimates:send\`.\
> \
> Returns \[the estimate reminder object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate-reminder).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`customer\_comms\_suppressed\`]\(<https://docs.shortcutco.com/developers/concepts/errors#customer\\_comms\\_suppressed>), \[\`estimate\_recently\_reminded\`]\(<https://docs.shortcutco.com/developers/concepts/errors#estimate\\_recently\\_reminded>), \[\`reminder\_in\_progress\`]\(<https://docs.shortcutco.com/developers/concepts/errors#reminder\\_in\\_progress>), \[\`reminder\_not\_sent\`]\(<https://docs.shortcutco.com/developers/concepts/errors#reminder\\_not\\_sent>), \[\`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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates:send","estimates: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."}},"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":{"EstimateReminder":{"type":"object","properties":{"object":{"type":"string","const":"estimate_reminder","description":"Always `estimate_reminder`."},"estimateId":{"type":"string","format":"uuid","description":"The estimate reminded about."},"remindedAt":{"type":"string","format":"date-time","description":"When the reminder went out, ISO 8601 in UTC."},"sentChannels":{"type":"array","items":{"type":"string","enum":["email","sms"]},"description":"Where the reminder went."},"skippedChannels":{"type":"array","items":{"type":"string"},"description":"Why a requested channel was skipped, one sentence each."}},"required":["object","estimateId","remindedAt","sentChannels","skippedChannels"],"additionalProperties":false,"description":"A reminder sent to the customer about an estimate."},"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/estimates/{id}/remind":{"post":{"operationId":"estimates.remind","tags":["Estimates"],"summary":"Remind about an estimate","description":"Reminds the customer about a pending estimate by email, text, or both, as Remind does in the app. Without `sendEmail` or `sendSms` the reminder uses the channels of the latest send. Suppressed and complaint-suppressed addresses and customers who opted out of texts are skipped, and `skippedChannels` says why. At most one reminder per estimate in ten minutes (`429 estimate_recently_reminded`). Repeating a request with the same `Idempotency-Key` returns the first reminder with `meta.alreadyReminded: true` once it was sent, however late. While the first attempt is still sending, the repeat gets a retryable `409 reminder_in_progress` with `Retry-After`; if that attempt stopped before finishing, a repeat five minutes after it started sends the reminder, so a reminder interrupted mid-send can reach the customer twice. Needs `estimates:send`.\n\nReturns [the estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder).\n\n**Errors specific to this endpoint:** [`customer_comms_suppressed`](https://docs.shortcutco.com/developers/concepts/errors#customer_comms_suppressed), [`estimate_recently_reminded`](https://docs.shortcutco.com/developers/concepts/errors#estimate_recently_reminded), [`reminder_in_progress`](https://docs.shortcutco.com/developers/concepts/errors#reminder_in_progress), [`reminder_not_sent`](https://docs.shortcutco.com/developers/concepts/errors#reminder_not_sent), [`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":{"sendEmail":{"type":"boolean","description":"Email a reminder. Defaults to the channels of the latest send."},"sendSms":{"type":"boolean","description":"Text a reminder. Defaults to the channels of the latest send."}},"additionalProperties":false,"description":"How to remind the customer."}}}},"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/EstimateReminder"},"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"}}}}}}
```

## Send an estimate

> Sends an estimate to the customer by email, text, or both, exactly as Send does in the app: a draft becomes \`pending\`, the lead becomes a prospect, and your estimate-sent automation delivers the link. Workspace email settings, unsubscribed and complaint-suppressed addresses, and customers who opted out of texts are honored as in the app. Sending a pending estimate again does not send a second message. Needs \`estimates:send\`. Other automations, such as those for the lead's new stage or on sent estimates, run only if the token has \`automations:trigger\`.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`customer\_comms\_suppressed\`]\(<https://docs.shortcutco.com/developers/concepts/errors#customer\\_comms\\_suppressed>), \[\`estimate\_not\_sendable\`]\(<https://docs.shortcutco.com/developers/concepts/errors#estimate\\_not\\_sendable>), \[\`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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates:send","estimates: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."}},"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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates/{id}/send":{"post":{"operationId":"estimates.send","tags":["Estimates"],"summary":"Send an estimate","description":"Sends an estimate to the customer by email, text, or both, exactly as Send does in the app: a draft becomes `pending`, the lead becomes a prospect, and your estimate-sent automation delivers the link. Workspace email settings, unsubscribed and complaint-suppressed addresses, and customers who opted out of texts are honored as in the app. Sending a pending estimate again does not send a second message. Needs `estimates:send`. Other automations, such as those for the lead's new stage or on sent estimates, run only if the token has `automations:trigger`.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).\n\n**Errors specific to this endpoint:** [`customer_comms_suppressed`](https://docs.shortcutco.com/developers/concepts/errors#customer_comms_suppressed), [`estimate_not_sendable`](https://docs.shortcutco.com/developers/concepts/errors#estimate_not_sendable), [`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":{"sendEmail":{"type":"boolean","default":true,"description":"Email the estimate link. Defaults to true."},"sendSms":{"type":"boolean","default":false,"description":"Text the estimate link. Customers who opted out of texts are skipped. Defaults to false."},"message":{"type":"string","maxLength":4000,"description":"Replaces the message to the customer."}},"additionalProperties":false,"description":"How to send the estimate."}}}},"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/EstimateWithLines"},"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"}}}}}}
```

## Void an estimate

> Voids an estimate, as Void does in the app: the customer can no longer approve it. Voiding a voided, declined, or expired estimate is \`409 state\_conflict\`. Jobs an approved estimate already created are not changed. Your voided-estimate automations run only if the token has \`automations:trigger\`.\
> \
> Returns \[the estimate object]\(<https://docs.shortcutco.com/developers/reference/objects/estimate).\\>
> \
> \*\*Errors specific to this endpoint:\*\* \[\`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":"Estimates","description":"An estimate offers lines of catalog services, each with a cadence and optional price options and products. Approving one creates its jobs.\n\nFields: [The estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate) · [The estimate reminder object](https://docs.shortcutco.com/developers/reference/objects/estimate-reminder)."}],"servers":[{"url":"https://api.shortcutco.com","description":"Production"}],"security":[{"bearerAuth":["estimates: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":{"EstimateWithLines":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The estimate's id."},"object":{"type":"string","const":"estimate","description":"Always `estimate`."},"status":{"type":"string","enum":["draft","pending","approved","declined","voided","expired"],"description":"`draft` until sent; `pending` once sent and waiting for the customer; then `approved`, `declined`, `voided` by the office, or `expired`."},"customerId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The customer the estimate is for, or null."},"leadId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The lead the estimate answers, or null."},"propertyId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The property the work is at, or null."},"propertyLabel":{"type":["string","null"],"description":"The property's label when the estimate was written, or null."},"propertyAddress":{"type":["string","null"],"description":"The property's address when the estimate was written, or null."},"message":{"type":["string","null"],"description":"The message to the customer sent with the estimate, or null."},"total":{"type":["number","null"],"description":"The estimate's total in US dollars as the app shows it, or null before it is priced."},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was last sent, ISO 8601 in UTC, or null."},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate stops being approvable, ISO 8601 in UTC, or null."},"expiredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate expired, ISO 8601 in UTC, or null."},"approvedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the estimate was approved, ISO 8601 in UTC, or null."},"declinedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"When the customer declined it, ISO 8601 in UTC, or null."},"declineReason":{"type":["string","null"],"description":"What the customer said when declining, or null."},"createdAt":{"type":"string","format":"date-time","description":"When the estimate was created, ISO 8601 in UTC."},"updatedAt":{"type":"string","format":"date-time","description":"When the estimate last changed, ISO 8601 in UTC."},"lines":{"type":"array","items":{"$ref":"#/components/schemas/EstimateLine"},"description":"The estimate's lines, in order."}},"required":["id","object","status","customerId","leadId","propertyId","propertyLabel","propertyAddress","message","total","sentAt","expiresAt","expiredAt","approvedAt","declinedAt","declineReason","createdAt","updatedAt","lines"],"additionalProperties":false,"description":"An estimate with its lines."},"EstimateLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The line's id."},"serviceId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The catalog service the line was made from, or null for a custom line."},"name":{"type":"string","description":"The line's name."},"description":{"type":["string","null"],"description":"The line's description, or null."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged: per hour, per square foot, flat, or per crew member-hour."},"rate":{"type":"number","description":"The price in US dollars, per `priceStructure`."},"quantity":{"type":["number","null"],"description":"Hours, square feet, or similar that `rate` multiplies, or null."},"subtotal":{"type":"number","description":"The line's price per visit in US dollars."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"`recurring`, `one_off`, or `on_demand` (snow work that waits for a storm)."},"cadenceFrequency":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"With `cadencePeriod`, how many visits per period, or null."},"cadencePeriod":{"anyOf":[{"type":"string","enum":["per_week","per_month","per_year","per_day"]},{"type":"null"}],"description":"The period `cadenceFrequency` counts in, or null."},"choiceGroupId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Lines sharing this id are alternatives the customer chooses between, or null."},"isOptional":{"type":"boolean","description":"Whether the customer may leave the line out."},"isRemovedByClient":{"type":"boolean","description":"Whether the customer left the line out when approving."},"selectedOptionId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"The option the customer chose, or null."},"sortOrder":{"type":"integer","description":"Position on the estimate, ascending."},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The option's id."},"name":{"type":"string","description":"The option's name."},"rate":{"type":"number","description":"The option's price in US dollars, per `priceStructure`."},"priceStructure":{"type":"string","enum":["per_hour","per_sqft","flat","per_man_hour"],"description":"How `rate` is charged."},"cadence":{"type":"string","enum":["one_off","recurring","on_demand"],"description":"How often the option's work happens."}},"required":["id","name","rate","priceStructure","cadence"],"additionalProperties":false},"description":"Price options the customer can pick between."},"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"The product line's id."},"productId":{"type":"string","format":"uuid","description":"The product from the catalog."},"name":{"type":"string","description":"The product's name on the estimate."},"description":{"type":["string","null"],"description":"The product's description, or null."},"unitLabel":{"type":"string","description":"The unit the quantity counts, such as `bag`."},"quantity":{"type":"number","description":"How many units."},"unitPrice":{"type":"number","description":"The customer's price per unit in US dollars."},"taxable":{"type":"boolean","description":"Whether sales tax applies."},"display":{"type":"string","enum":["itemized","included"],"description":"`itemized` shows the product as its own line to the customer; `included` folds it into the service's price."}},"required":["id","productId","name","description","unitLabel","quantity","unitPrice","taxable","display"],"additionalProperties":false},"description":"Products sold with the line."}},"required":["id","serviceId","name","description","priceStructure","rate","quantity","subtotal","cadence","cadenceFrequency","cadencePeriod","choiceGroupId","isOptional","isRemovedByClient","selectedOptionId","sortOrder","options","products"],"additionalProperties":false,"description":"A line of an estimate."},"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/estimates/{id}/void":{"post":{"operationId":"estimates.void","tags":["Estimates"],"summary":"Void an estimate","description":"Voids an estimate, as Void does in the app: the customer can no longer approve it. Voiding a voided, declined, or expired estimate is `409 state_conflict`. Jobs an approved estimate already created are not changed. Your voided-estimate automations run only if the token has `automations:trigger`.\n\nReturns [the estimate object](https://docs.shortcutco.com/developers/reference/objects/estimate).\n\n**Errors specific to this endpoint:** [`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/EstimateWithLines"},"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"}}}}}}
```
