Estimates
An estimate describes proposed products or services, taxes, pricing, and an expiry date for a customer. API v3 can create, update, convert, and delete eligible open estimates and retrieve existing active estimates.
The estimate object
| Field | Type | Description |
|---|---|---|
id | string | Unique estimate UUID. |
object | string | Always estimate. |
estimate_number | integer | Account-assigned estimate number. |
custom_estimate_number | string or null | Optional custom estimate number. |
name | string or null | Estimate name. |
customer_id | string | Customer UUID. |
customer_name | string | Customer display name stored with the estimate. |
salesperson_id | string | Assigned SalesBinder user UUID. |
status_id | integer | Current estimate status identifier. |
status | string or null | Current estimate status name. |
issue_date | string or null | Estimate issue date in YYYY-MM-DD format. |
expiry_date | string or null | Estimate expiry date in YYYY-MM-DD format. |
date_sent | string or null | Date sent in YYYY-MM-DD format. |
purchase_order_number | string | Customer purchase-order reference. |
attention | string or null | Attention line. |
shipping_address | string or null | Shipping-address text stored on the estimate. |
public_note | string or null | Customer-facing estimate note. |
payment_terms | string or null | Payment terms stored on the estimate. |
subtotal | string | Subtotal before shipping and taxes, as a four-decimal string. |
shipping | string | Shipping charge as a four-decimal string. |
tax | string | Primary tax amount as a four-decimal string. |
tax_2 | string | Secondary tax amount as a four-decimal string. |
total | string | Estimate total including shipping and taxes, as a four-decimal string. |
created_at | string or null | ISO 8601 creation timestamp. |
updated_at | string or null | ISO 8601 modification timestamp. |
lines | array | Current estimate line objects. Present only when retrieving one estimate. |
Monetary values use the SalesBinder account's base currency. Cost and margin values are not included in estimate API responses.
The estimate line object
| Field | Type | Description |
|---|---|---|
id | string | Unique estimate-line UUID. |
object | string | Always estimate_line. |
item_id | string or null | Related inventory item UUID, when present. |
name | string or null | Line-item name. |
description | string or null | Line-item description. |
quantity | number | Proposed quantity. |
unit_id | integer or null | Related unit identifier. |
unit_price | string | Original unit price as a four-decimal string. |
discount_percent | string | Discount percentage as a three-decimal string. |
discounted_unit_price | string or null | Discounted unit price, or null when no discounted price is stored. |
tax_rate | string | Primary tax percentage as a three-decimal string. |
tax_2_rate | string | Secondary tax percentage as a three-decimal string. |
subtotal | string | Quantity multiplied by the effective unit price, before tax, as a four-decimal string. |
Line-item unit costs are intentionally omitted.
Create an estimate
POST/api/v3/estimates
Required scope: estimates:write
Creates an open estimate assigned to the API key's current user. The response is the new estimate object with its calculated line items. Creating an estimate does not send it to the customer, reserve or deduct inventory, or convert it into an invoice or sales order.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
customer_id | string | Yes | Active customer UUID in the current SalesBinder account. |
lines | array | Yes | One to 100 inventory or service line objects. |
name | string | No | Optional name for your own reference. |
issue_date | string | No | Issue date in YYYY-MM-DD format. Defaults using the account and user timezone. |
expiry_date | string | No | Expiry date in YYYY-MM-DD format. Defaults from the issue date using the account's estimate-expiry setting. |
custom_estimate_number | string | No | Optional custom reference, up to 24 characters. |
purchase_order_number | string | No | Customer purchase-order reference. |
attention | string | No | Attention line. |
shipping_address | string | No | Shipping-address text. |
public_note | string | No | Customer-facing note. |
payment_terms | string | No | Payment terms. Account defaults apply when omitted. |
shipping | number | No | Shipping charge. Defaults to 0. |
discount_percent | number | No | Estimate-wide discount from 0 to 100. |
An explicitly supplied expiry_date cannot be before issue_date.
Line fields
| Field | Type | Required | Description |
|---|---|---|---|
item_id | string | Inventory lines | Active inventory item UUID. The API copies its name, description, cost, and permitted selling price. |
item_variation_location_id | integer | Variation items | Variation-location identifier belonging to the selected item and account. |
name | string | Service lines | Service line name. Ignored for inventory lines. |
description | string | No | Service line description. Inventory descriptions come from the item. |
quantity | number | No | Positive quantity. Defaults to 1; unique items are always 1. |
unit_price | number | Service lines | Required for service lines. Optional for inventory lines only when the current user may modify selling prices. |
tax_rate | number | No | Primary tax percentage from 0 to 100; defaults to the customer's rate. |
tax_2_rate | number | No | Secondary tax percentage from 0 to 100; defaults to the customer's rate. |
Estimate number, status, salesperson, account, source, totals, costs, conversion state, and timestamps are controlled by SalesBinder. Supplying unsupported or protected fields returns 422 unsupported_estimate_field rather than silently accepting them.
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates" \
--request POST \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: warehouse-estimate-2042" \
--data '{
"customer_id": "709d2a43-12a9-4d85-a9d9-cb16e66cef53",
"issue_date": "2026-07-19",
"shipping": 10,
"discount_percent": 5,
"lines": [
{
"item_id": "c420fb4b-e98e-4ed4-b2e9-7ad8cc970966",
"quantity": 2
},
{
"name": "Installation",
"quantity": 1,
"unit_price": 75,
"tax_rate": 5
}
]
}'The estimate and its number are saved together. A failed request does not consume an estimate number. Creating an estimate does not change on-hand quantities.
Update an estimate
PATCH/api/v3/estimates/{estimate_id}
Required scope: estimates:write
Updates supported fields on an existing open estimate. Detail fields are partial: fields omitted from the request keep their existing values. SalesBinder recalculates protected totals and estimate quantities automatically. Updating an estimate does not change on-hand inventory.
Only estimates with the Open status can be updated. Declined, expired, closed, and converted estimates are protected; use the separate conversion endpoints documented below.
Updatable fields
| Field | Type | Description |
|---|---|---|
customer_id | string | Active customer UUID in the current account. Changing the customer requires a complete lines replacement. |
name | string | Optional name for your own reference. |
issue_date | string | Issue date in YYYY-MM-DD format. |
expiry_date | string | Future expiry date in YYYY-MM-DD format; cannot be before issue_date. |
custom_estimate_number | string | Optional custom reference, up to 24 characters. |
purchase_order_number | string | Customer purchase-order reference. |
attention | string | Attention line. |
shipping_address | string | Shipping-address text. |
public_note | string | Customer-facing note. |
payment_terms | string | Payment terms. |
shipping | number | Non-negative shipping charge. |
discount_percent | number | Estimate-wide discount from 0 to 100. |
lines | array | Complete replacement for the estimate's active non-discount lines. |
Estimate number, status, salesperson, account, source, totals, costs, conversion state, and timestamps cannot be changed with this endpoint.
Replacing lines
When lines is omitted, current lines remain unchanged. When lines is included, it is the complete desired set of active non-discount lines:
- Include an existing line's
idto update that line. - Omit
idto add a new line. - Existing lines omitted from the array are removed from the active estimate.
- A line ID must belong to the same active estimate and can appear only once.
- Set the estimate-wide discount with
discount_percent; do not include the generated discount line inlines.
Each replacement line accepts the same inventory and service fields described under Line fields. At least one line is required when replacing lines.
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513" \
--request PATCH \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: revise-estimate-2042" \
--data '{
"expiry_date": "2026-09-15",
"shipping": 10,
"discount_percent": 5,
"lines": [
{
"id": "d96bd8aa-3131-45a8-a6c2-7ab74b79b93a",
"name": "Installation",
"quantity": 2,
"unit_price": 75,
"tax_rate": 5
},
{
"item_id": "c420fb4b-e98e-4ed4-b2e9-7ad8cc970966",
"quantity": 1
}
]
}'List estimates
GET/api/v3/estimates
Required scope: estimates:read
Returns visible active estimates ordered by estimate number, newest first. Line items are omitted from list responses.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | integer | No | Page number. Defaults to 1. |
limit | integer | No | Records per page. Defaults to 20; maximum 100. |
q | string | No | Searches customer name, custom estimate number, and estimate name. A numeric value also matches an exact estimate number. |
customer_id | string | No | Returns estimates for the customer UUID. |
status_id | integer | No | Returns estimates with the status identifier. |
issue_date_from | string | No | Includes estimates issued on or after this YYYY-MM-DD date. |
issue_date_to | string | No | Includes estimates issued on or before this YYYY-MM-DD date. |
expiry_date_from | string | No | Includes estimates expiring on or after this YYYY-MM-DD date. |
expiry_date_to | string | No | Includes estimates expiring on or before this YYYY-MM-DD date. |
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates?customer_id=709d2a43-12a9-4d85-a9d9-cb16e66cef53&limit=20" \
--header "Authorization: Bearer YOUR_API_KEY"Example response
{
"object": "list",
"url": "/api/v3/estimates",
"has_more": false,
"data": [
{
"id": "c40e5d25-c573-48ec-aa46-9737eddf2513",
"object": "estimate",
"estimate_number": 2042,
"customer_id": "709d2a43-12a9-4d85-a9d9-cb16e66cef53",
"customer_name": "Example Customer",
"status_id": 5,
"status": "Open",
"issue_date": "2026-07-18",
"expiry_date": "2026-08-17",
"subtotal": "100.0000",
"shipping": "10.0000",
"tax": "5.5000",
"tax_2": "2.2000",
"total": "117.7000"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total_pages": 1,
"total_records": 1
}
}Examples may omit unchanged fields for readability.
Retrieve an estimate
GET/api/v3/estimates/{estimate_id}
Required scope: estimates:read
Returns one visible active estimate. The response includes the lines array, ordered by the estimate's stored line order. Deleted lines are excluded.
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513" \
--header "Authorization: Bearer YOUR_API_KEY"If the estimate is outside the current account or assigned-user boundary, is not an estimate, has been archived, or does not exist, the API returns 404 resource_missing.
Convert an estimate to an invoice
POST/api/v3/estimates/{estimate_id}/convert-to-invoice
Required scope: estimates:convert
Converts one visible, open, unconverted estimate into a new unpaid invoice. SalesBinder copies the customer, salesperson, supported details, inventory and service lines, prices, discounts, taxes, shipping, and totals. The estimate is closed and linked to the new invoice, and the invoice is linked back to its source estimate.
The optional request body accepts one field:
| Field | Type | Required | Description |
|---|---|---|---|
issue_date | string | No | New invoice issue date in YYYY-MM-DD format. Defaults to the current date in the API user's account timezone. |
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513/convert-to-invoice" \
--request POST \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: convert-estimate-2042" \
--data '{
"issue_date": "2026-07-20"
}'The response is the complete new invoice object, including its assigned invoice number and lines, with HTTP 201 Created.
The action requires both the user's current estimate-modify and invoice-modify permissions and follows the account's record limit. A prospect associated with the estimate is converted to a customer. Related inventory updates begin only after the conversion succeeds.
An Idempotency-Key is optional but recommended. During the short replay window, retrying the same conversion with the same key and request returns the original 201 response with Idempotent-Replayed: true. Reusing the key with different request data returns a conflict. A later conversion attempt after the estimate has already closed returns 409 estimate_conversion_not_allowed.
Convert an estimate to a sales order
POST/api/v3/estimates/{estimate_id}/convert-to-sales-order
Required scope: estimates:convert_to_sales_order
Converts one visible, open, unconverted estimate into a new open sales order. SalesBinder copies the customer, supported estimate details, inventory and service lines, prices, discounts, taxes, shipping, and totals. The estimate is closed and linked to the new sales order, and the sales order links back to its source estimate.
The request body must be empty. SalesBinder assigns the sales-order number and current issue date.
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513/convert-to-sales-order" \
--request POST \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: convert-estimate-to-order-2042" \
--data '{}'The response is the complete new sales-order object, including its assigned sales-order number and lines, with HTTP 201 Created.
The action requires the user's current estimate-modify, sales-order-list, and sales-order-modify permissions and follows the account's record limit. Related inventory updates begin only after the conversion succeeds.
An Idempotency-Key is optional but recommended. During the short replay window, retrying the same conversion with the same key returns the original 201 response with Idempotent-Replayed: true. A later conversion attempt after the estimate has closed returns 409 estimate_sales_order_conversion_not_allowed.
Decline an estimate
POST/api/v3/estimates/{estimate_id}/decline
Required scope: estimates:decline
Marks one visible, open, unconverted estimate as declined while preserving the estimate and its lines. Declining requires the current user's estimate-list and estimate-modify permissions.
The request body must be an empty JSON object. The general estimate update endpoint cannot set status directly.
Example request
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513/decline" \
--request POST \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: decline-estimate-c40e5d25" \
--data '{}'The response is the complete estimate detail object with status 7 (Declined). Repeating the action for an already-declined estimate is safe. An optional Idempotency-Key also replays the original response during the short retry window.
SalesBinder returns 409 estimate_decline_not_allowed for closed, expired, converted, or otherwise ineligible estimates. Declining refreshes estimate availability without changing on-hand inventory.
Delete an estimate
DELETE/api/v3/estimates/{estimate_id}
Required scope: estimates:delete
Permanently deletes an active estimate and its lines when the estimate is still open and has not been converted into an invoice or sales order.
curl "https://yourbusiness.salesbinder.com/api/v3/estimates/c40e5d25-c573-48ec-aa46-9737eddf2513" \
--request DELETE \
--header "Authorization: Bearer YOUR_API_KEY"{
"id": "c40e5d25-c573-48ec-aa46-9737eddf2513",
"object": "estimate",
"deleted": true
}The endpoint returns 409 Conflict for closed, declined, expired, converted, or otherwise ineligible estimates. It does not delete an invoice or sales order created from the estimate. Estimate deletion follows the current user's delete permission and assigned-user visibility.
SalesBinder deletes the eligible estimate and its lines together and refreshes related account totals and availability. On-hand inventory quantity is not changed.
Permissions and write behavior
API key scopes can reduce access but cannot exceed the current user's SalesBinder permissions. The user must currently be able to list estimates. Without the current view-all-estimates permission, list and retrieve operations are limited to estimates assigned to that user.
Creating, updating, or declining an estimate also requires the current estimate-modify permission. Invoice conversion requires the current estimate-modify and invoice-modify permissions. Sales-order conversion requires the current estimate-modify, sales-order-list, and sales-order-modify permissions. Both conversions require account record capacity for the new record. Deleting requires the current estimate-delete permission. Inventory selling-price overrides require the current modify-selling-prices permission, and location-limited users can only select inventory in their permitted location.
API v3 does not support sending, reopening, archiving, or restoring estimates. Unsupported fields and actions are rejected.