Base URL: https://couplrfsm.com/api/v1. Conventions shared by every endpoint (examples, ids, dates, money) are on the API reference page. Errors, pagination and idempotency are in Errors, limits and retries.
List estimates#
GET /api/v1/estimates · requires the estimates:read ability
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Default: 1. |
per_page |
integer | No | Values above 100 are served as 100. Default: 25. |
updated_since |
string (date-time) | No | Only records changed at or after this time. ISO 8601, for example 2026-11-14T15:00:00Z. |
customer_id |
integer | No | |
status |
string: open, accepted, declined |
No | open, accepted or declined. Any other value is ignored. |
Example request
curl "https://couplrfsm.com/api/v1/estimates" \
-H "Authorization: Bearer YOUR_API_KEY"
Example response (200)
{
"data": [
{
"id": 511,
"layout": "simple",
"title": "Replace condenser fan motor",
"customer_id": 1842,
"customer_name": "Dana Whitfield",
"status": "accepted",
"total": 389,
"accepted_at": "2026-12-01T16:45:00+00:00",
"declined_at": null,
"created_at": "2026-11-30T14:10:00+00:00",
"updated_at": "2026-12-01T16:45:00+00:00"
}
],
"meta": {
"page": 1,
"per_page": 25,
"total": 1,
"last_page": 1
}
}
Errors
| Status | When |
|---|---|
401 |
No key (api_key_missing), the key is unknown or revoked (api_key_invalid), the key had an end date that has passed (api_key_expired), or the key belongs to a private trial sandbox whose access window has ended (sandbox_expired). |
403 |
The key lacks the ability this endpoint needs (ability_missing), no admin user is left to act for the key (no_acting_admin), or the company's account is not yet verified. |
422 |
A parameter or field is invalid (validation_failed). errors maps each field to its messages. |
429 |
Too many requests (rate_limited). Wait Retry-After seconds. |
Create an estimate#
POST /api/v1/estimates · requires the estimates:write ability · accepts Idempotency-Key
Creates an estimate with the simple layout: one list of items. It
is a draft unless you send "send": true, in which case Couplr
emails it to the customer as soon as it is ready. The customer reads
and accepts it on Couplr's page. Nothing is charged.
A line with price_book_item_id uses that price book item and its
name. A line without one uses the price book item of the same name,
or adds the name to the price book as a new item. An item can appear
once: for two of the same thing, send one line with a quantity.
Discounts, deposits, maintenance plans and the sectioned layout
are set in Couplr and cannot be created here. Sales tax follows the
account's tax settings.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key |
string | No | Makes a retry of this request safe. See "Retries and idempotency". |
Request body (application/json)
Fields not listed are ignored. The estimate always has the simple layout.
| Field | Type | Required | Description |
|---|---|---|---|
customer_id |
integer | Yes | |
title |
string or null | No | A name for the estimate. It is shown on the estimate in Couplr's lists and search. |
notes |
string or null | No | |
send |
boolean | No | Email the estimate to the customer once it is ready. The customer must have an email address. Default: false. |
line_items |
array of objects | Yes | |
line_items[].price_book_item_id |
integer or null | No | An existing price book item. When sent, name is not used. |
line_items[].name |
string or null | No | Required unless price_book_item_id is sent. |
line_items[].quantity |
integer | No | A whole number. Default: 1. |
line_items[].unit_price |
number or null | No | Dollars. Required unless price_book_item_id is sent, when it defaults to the item's price. |
Example request
curl -X POST "https://couplrfsm.com/api/v1/estimates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-d '{"customer_id":1842,"title":"New condenser fan motor","notes":"Price includes disposal of the old motor.","line_items":[{"name":"Condenser fan motor","quantity":1,"unit_price":289},{"price_book_item_id":771,"quantity":1}]}'
Example response (201)
{
"data": {
"id": 511,
"layout": "simple",
"title": "Replace condenser fan motor",
"customer_id": 1842,
"customer_name": "Dana Whitfield",
"status": "accepted",
"total": 389,
"accepted_at": "2026-12-01T16:45:00+00:00",
"declined_at": null,
"created_at": "2026-11-30T14:10:00+00:00",
"updated_at": "2026-12-01T16:45:00+00:00"
}
}
Errors
| Status | When |
|---|---|
401 |
No key (api_key_missing), the key is unknown or revoked (api_key_invalid), the key had an end date that has passed (api_key_expired), or the key belongs to a private trial sandbox whose access window has ended (sandbox_expired). |
403 |
The key lacks the ability this endpoint needs (ability_missing), no admin user is left to act for the key (no_acting_admin), or the company's account is not yet verified. |
422 |
A parameter or field is invalid (validation_failed). errors maps each field to its messages. |
429 |
Too many requests (rate_limited). Wait Retry-After seconds. |
Get an estimate#
GET /api/v1/estimates/{id} · requires the estimates:read ability
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes |
Example request
curl "https://couplrfsm.com/api/v1/estimates/1842" \
-H "Authorization: Bearer YOUR_API_KEY"
Example response (200)
{
"data": {
"id": 511,
"layout": "simple",
"title": "Replace condenser fan motor",
"customer_id": 1842,
"customer_name": "Dana Whitfield",
"status": "accepted",
"total": 389,
"accepted_at": "2026-12-01T16:45:00+00:00",
"declined_at": null,
"created_at": "2026-11-30T14:10:00+00:00",
"updated_at": "2026-12-01T16:45:00+00:00"
}
}
Errors
| Status | When |
|---|---|
401 |
No key (api_key_missing), the key is unknown or revoked (api_key_invalid), the key had an end date that has passed (api_key_expired), or the key belongs to a private trial sandbox whose access window has ended (sandbox_expired). |
403 |
The key lacks the ability this endpoint needs (ability_missing), no admin user is left to act for the key (no_acting_admin), or the company's account is not yet verified. |
404 |
No such record in this company (not_found). |
429 |
Too many requests (rate_limited). Wait Retry-After seconds. |
Send an estimate#
POST /api/v1/estimates/{id}/send · requires the estimates:write ability · accepts Idempotency-Key
Emails the estimate to its customer, the same as "Send" in the apps. There is no request body. An estimate can be sent more than once.
Either layout can be sent. A sectioned estimate also goes to the
extra recipients saved on it in Couplr, and its status can still
read draft afterwards.
Refused with 422 on estimate when: it has already been
accepted or declined; the customer has no email address; or it was
created a moment ago and is still being prepared, in which case try
again shortly.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key |
string | No | Makes a retry of this request safe. See "Retries and idempotency". |
Example request
curl -X POST "https://couplrfsm.com/api/v1/estimates/1842/send" \
-H "Authorization: Bearer YOUR_API_KEY"
Example response (200)
{
"data": {
"id": 511,
"layout": "simple",
"title": "New condenser fan motor",
"customer_id": 1842,
"customer_name": "Dana Whitfield",
"status": "sent",
"total": 389,
"accepted_at": null,
"declined_at": null,
"created_at": "2026-11-30T14:10:00+00:00",
"updated_at": "2026-11-30T14:12:00+00:00",
"line_items": [
{
"name": "Condenser fan motor",
"quantity": 1,
"rate": "289.00"
},
{
"name": "Service call",
"quantity": 1,
"rate": "100.00"
}
]
}
}
Errors
| Status | When |
|---|---|
401 |
No key (api_key_missing), the key is unknown or revoked (api_key_invalid), the key had an end date that has passed (api_key_expired), or the key belongs to a private trial sandbox whose access window has ended (sandbox_expired). |
403 |
The key lacks the ability this endpoint needs (ability_missing), no admin user is left to act for the key (no_acting_admin), or the company's account is not yet verified. |
404 |
No such record in this company (not_found). |
422 |
A parameter or field is invalid (validation_failed). errors maps each field to its messages. |
429 |
Too many requests (rate_limited). Wait Retry-After seconds. |
Estimate object#
| Field | Type | Description |
|---|---|---|
id |
integer | |
layout |
string: simple, sectioned |
simple is one list of items. sectioned groups the items under headings. |
title |
string or null | |
customer_id |
integer | |
customer_name |
string or null | |
status |
string: draft, sent, accepted, declined |
|
total |
number or null | Dollars. |
accepted_at |
string (date-time) or null | |
declined_at |
string (date-time) or null | |
created_at |
string (date-time) or null | |
updated_at |
string (date-time) or null | |
line_items |
array of objects | On a single estimate with the simple layout only, not in lists. |
line_items[].name |
string or null | |
line_items[].quantity |
number or null | |
line_items[].rate |
string or null | Decimal string in dollars. |