Source: https://billingmart.com/developers/api/estimates # Estimates Quotes before the work, converted into an invoice once the client accepts. [All sections](https://billingmart.com/developers/api) [Authentication](https://billingmart.com/developers/authentication) **On this page** - GET List - POST Create - GET Get - PATCH Update - POST Convert - POST Discard - OBJ Estimate On this page - GET List - POST Create - GET Get - PATCH Update - POST Convert - POST Discard - OBJ Estimate estimates ## List GET `/v1/estimates` List estimates, newest first. A discarded draft is left out unless discarded=true. Scope `estimates:read` Effect Read only MCP tool `list_estimates` Retries Safe to repeat ### Parameters List parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `limit`optional | query | `integer` | min 1 · max 50 | | `cursor`optional | query | `string` | up to 256 chars | | `status`optional | query | `enum` | One of `draft`, `sent`, `viewed`, `accepted`, `declined`, `expired`, `converted`, `void` | | `clientId`optional | query | `id` | — | | `discarded`optional | query | `boolean` | — | ### Returns `data`: [`Estimate[]`](https://billingmart.com/developers/api/estimates#object-Estimate) . Pass `meta.cursor` back as `cursor` for the next page. Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/estimates" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` estimates ## Create POST `/v1/estimates` Create a draft estimate. Scope `estimates:write` Effect Writes a draft or record MCP tool `create_estimate` Retries Send an Idempotency-Key; a repeat replays the stored answer for 24 hours ### Request body JSON. Money is an integer count of minor units beside its currency; quantities are thousandths. Create request body | Field | Type | Notes | | --- | --- | --- | | `businessId`required | `id` | — | | `clientId`required | `id` | — | | `currency`optional | `Currency` | — | | `language`optional | `string` | up to 8 chars | | `lineItems[]`optional | `object[]` | up to 200 items | | `lineItems[].id`optional | `id` | — | | `lineItems[].description`optional | `string` | What the line is for. Optional with a presetKey (the item’s name is used). — up to 2000 chars | | `lineItems[].quantityMilli`optional | `integer` | Quantity in thousandths: 1000 is one unit, 1500 is 1.5 hours. Defaults to 1000. | | `lineItems[].rateBasis`optional | `enum` | How the price applies: fixed for one flat charge, hourly, daily, perUnit, percentage… Optional with a presetKey. — One of `fixed`, `hourly`, `daily`, `weekly`, `monthly`, `perUnit`, `perMile`, `perKilometre`, `percentage` | | `lineItems[].unitPrice`optional | `object` | Price per unit in integer minor units of the document currency ($5,000.00 is 500000). | | `lineItems[].unitPrice.minorUnits`required | `integer` | — | | `lineItems[].unitPrice.currency`required | `Currency` | — | | `lineItems[].rateBasisPoints`optional | `integer` | — | | `lineItems[].taxTreatment`optional | `string` | — | | `lineItems[].taxRateBasisPoints`optional | `integer` | — | | `lineItems[].discountBasisPoints`optional | `integer` | — | | `lineItems[].presetKey`optional | `string` | The key of one of the business’s products and services (get_business_context lists them); the server fills what the line leaves out. — up to 64 chars | | `notes`optional | `string` | up to 4000 chars | | `issueDate`optional | `string` | matches `^\d{4}-\d{2}-\d{2}$` | | `validUntil`optional | `string` | matches `^\d{4}-\d{2}-\d{2}$` | ### Returns `data`: `object` Example request Sample ``` curl -X POST "https://api.billingmart.com/v1/estimates" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{"businessId":"","clientId":""}' ``` estimates ## Get GET `/v1/estimates/{estimateId}` One estimate. Scope `estimates:read` Effect Read only MCP tool `get_estimate` Retries Safe to repeat ### Parameters Get parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `estimateId`required | path | `id` | — | ### Returns `data`: [`Estimate`](https://billingmart.com/developers/api/estimates#object-Estimate) Example request Sample ``` curl -X GET "https://api.billingmart.com/v1/estimates/{estimateId}" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` estimates ## Update PATCH `/v1/estimates/{estimateId}` Update a draft estimate. Scope `estimates:write` Effect Writes a draft or record MCP tool `update_estimate` Retries No Idempotency-Key on this route ### Parameters Update parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `estimateId`required | path | `id` | — | ### Request body JSON. Money is an integer count of minor units beside its currency; quantities are thousandths. Update request body | Field | Type | Notes | | --- | --- | --- | | `expectedUpdatedAt`optional | `string` | up to 40 chars | | `lineItems[]`optional | `object[]` | up to 200 items | | `lineItems[].id`optional | `id` | — | | `lineItems[].description`optional | `string` | What the line is for. Optional with a presetKey (the item’s name is used). — up to 2000 chars | | `lineItems[].quantityMilli`optional | `integer` | Quantity in thousandths: 1000 is one unit, 1500 is 1.5 hours. Defaults to 1000. | | `lineItems[].rateBasis`optional | `enum` | How the price applies: fixed for one flat charge, hourly, daily, perUnit, percentage… Optional with a presetKey. — One of `fixed`, `hourly`, `daily`, `weekly`, `monthly`, `perUnit`, `perMile`, `perKilometre`, `percentage` | | `lineItems[].unitPrice`optional | `object` | Price per unit in integer minor units of the document currency ($5,000.00 is 500000). | | `lineItems[].unitPrice.minorUnits`required | `integer` | — | | `lineItems[].unitPrice.currency`required | `Currency` | — | | `lineItems[].rateBasisPoints`optional | `integer` | — | | `lineItems[].taxTreatment`optional | `string` | — | | `lineItems[].taxRateBasisPoints`optional | `integer` | — | | `lineItems[].discountBasisPoints`optional | `integer` | — | | `lineItems[].presetKey`optional | `string` | The key of one of the business’s products and services (get_business_context lists them); the server fills what the line leaves out. — up to 64 chars | | `notes`optional | `string` | up to 4000 chars | | `issueDate`optional | `string` | matches `^\d{4}-\d{2}-\d{2}$` | | `validUntil`optional | `string` | matches `^\d{4}-\d{2}-\d{2}$` | ### Returns `data`: [`Estimate`](https://billingmart.com/developers/api/estimates#object-Estimate) Errors this route can answer besides the shared ones: `ACTION_PENDING_ON_TARGET` , `NOT_A_DRAFT` . Example request Sample ``` curl -X PATCH "https://api.billingmart.com/v1/estimates/{estimateId}" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" ``` estimates ## Convert POST `/v1/estimates/{estimateId}/convert` Convert an ACCEPTED estimate into a draft invoice (its lines re-derived). Nothing is sent. Refused unless the estimate is accepted; nothing in this API marks one accepted yet. Scope `estimates:write` Effect Writes a draft or record MCP tool `convert_estimate_to_invoice` Retries Send an Idempotency-Key; a repeat replays the stored answer for 24 hours ### Parameters Convert parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `estimateId`required | path | `id` | — | ### Returns `data`: `object` Errors this route can answer besides the shared ones: `INVALID_TRANSITION` . Example request Sample ``` curl -X POST "https://api.billingmart.com/v1/estimates/{estimateId}/convert" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` estimates ## Discard POST `/v1/estimates/{estimateId}/discard` Discard a never-numbered DRAFT estimate. Refused once numbered, or while an action on it is pending. Scope `estimates:write` Effect Writes a draft or record MCP tool `discard_draft_estimate` Retries Send an Idempotency-Key; a repeat replays the stored answer for 24 hours ### Parameters Discard parameters | Name | In | Type | Notes | | --- | --- | --- | --- | | `estimateId`required | path | `id` | — | ### Returns `data`: `object` Errors this route can answer besides the shared ones: `NOT_A_DRAFT` , `ACTION_PENDING_ON_TARGET` . Example request Sample ``` curl -X POST "https://api.billingmart.com/v1/estimates/{estimateId}/discard" \ -H "Authorization: Bearer $BILLINGMART_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" ``` Object ## Estimate A quote. Sending one counts as a send toward the plan’s monthly allowance and is never blocked, even at the limit. Acceptance does not create an invoice: conversion is a separate, human act, which is why convertedInvoiceId is optional and set by the server. Estimate fields | Field | Type | Notes | | --- | --- | --- | | `id`required | `Id` | — | | `tenant`required | `TenantRef` | — | | `number`optional | `string` | — | | `clientId`required | `Id` | — | | `client`required | `InvoiceClientSnapshot` | — | | `status`required | `EstimateStatus` | — | | `currency`required | `Currency` | — | | `language`required | `Language` | — | | `issueDate`optional | `DateOnly` | — | | `validUntil`optional | `DateOnly` | — | | `lineItems`required | `LineItem[]` | — | | `customFields`optional | `object` | Untrusted text key, exactly as on an Invoice: typed against the business model, unknown keys dropped, never free-form. | | `totals`required | `InvoiceTotals` | — | | `notes`optional | `string` | Untrusted text — | | `internalNotes`optional | `string` | Untrusted text INTERNAL. Never crosses to a public link. | | `terms`optional | `string` | Untrusted text — | | `acceptedAt`optional | `IsoTimestamp` | — | | `acceptedByName`optional | `string` | Untrusted text Who accepted, as typed by the customer. Evidence, not identity. | | `declinedAt`optional | `IsoTimestamp` | — | | `convertedInvoiceId`optional | `Id` | — | | `createdAt`required | `IsoTimestamp` | — | | `updatedAt`optional | `IsoTimestamp` | — | | `createdBy`optional | `Id` | — | | `discardedAt`optional | `IsoTimestamp` | . Set by a function when a never-numbered draft is discarded: it leaves every default list and cannot be sent or opened. Soft, never a status value (a money-enum addition is a breaking change). | | `discardedBy`optional | `Id` | . The uid or credential that discarded it. | | `lastWriteActor`optional | `WriteActor` | — | | `jobId`optional | `Id` | The work record this belongs to, when it does. FUNCTION-WRITTEN: set by the callables that check the record is in the same business, never by a payload alone. | Next section ## Payments What was paid, how, and what is still owed. Recording and refunding are API-key scopes or approved actions, never a connected assistant on its own. [Continue to Payments →](https://billingmart.com/developers/api/payments) ## Try it against your sandbox Create a bmk_test_ key in the console. It can only reach your sandbox workspace. [Start free](https://app.billingmart.com/sign-up) [All sections](https://billingmart.com/developers/api) --- This is the Markdown copy of https://billingmart.com/developers/api/estimates. Live plans, prices and limits are published as JSON at https://billingmart.com/plans.json and https://billingmart.com/agent-info.json.