FieldStacker API v1 · full reference

API reference: every route and field

Every route of the FieldStacker API, with its parameters and the fields it answers with. For the overview, keys and plans, see the API page. Machine-readable: the OpenAPI 3.1 JSON (no key needed), also as a static copy.

The URL. The route is in the query string: https://www.fieldstacker.com/app/api.php?route=v1/customers. Add other parameters with & (…?route=v1/customers&limit=20), not a second ?.

Auth. Authorization: Bearer fsk_… (or X-API-Key), made by the business owner in Settings → API & webhooks. 60 requests a minute per key.

Answers. {"ok": true, "data": …}; a list adds next_cursor and limit (pass cursor with the same filters for the next page, limit 1–100). Errors are {"ok": false, "error": {"code", "message", "field"}}. Times are ISO 8601 with your business's offset; money is a decimal string with currency. A POST takes an Idempotency-Key header.

For your own business only. The API connects your own FieldStacker account to your own systems. Building an app or service that other businesses use with FieldStacker is not allowed (API Terms). It never charges cards, refunds or marks anything paid, never returns costs, commission or hidden lines, and contracts are signed only on the customer's own page.

Sales & money

Customers · 4 routes
GETv1/customers

Customers, most recent activity first. Plan: Customers.

Parameters

NameInTypeNotes
qquerystringSearch name, business, phone (or its digits), email, address, tags across every record. Up to 100 characters.
updated_sincequerystringLast activity or edit at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstring or nullcus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id.
namestring
businessstring
phonestring
alt_phonestring
emailstring
addressstring
tagsarray of string
notesstring
record_idsarray of stringEvery one resolves to this customer.
recordsobject
records.quotesinteger
records.card_contactsinteger
records.estimates_and_invoicesinteger
records.appointmentsinteger
records.doorsintegerDoor trades only.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullLast activity or edit. ISO 8601 with the business's UTC offset.

Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/customers

Add a customer (like + Add customer). An existing match answers 200 existing: true and writes nothing.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

At least one of name, phone, email. lat and lng go together.

FieldTypeNotes
namestringUp to 120 characters.
phonestringUp to 40 characters.
emailemailUp to 160 characters.
addressstringUp to 300 characters.
notesstringUp to 500 characters.
latnumber-90 to 90.
lngnumber-180 to 180.

Answer 200

data is:

FieldTypeNotes
existing (next to data)true
idstring or nullcus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id.
namestring
businessstring
phonestring
alt_phonestring
emailstring
addressstring
tagsarray of string
notesstring
record_idsarray of stringEvery one resolves to this customer.
recordsobject
records.quotesinteger
records.card_contactsinteger
records.estimates_and_invoicesinteger
records.appointmentsinteger
records.doorsintegerDoor trades only.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullLast activity or edit. ISO 8601 with the business's UTC offset.

Errors: 400 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/customers/{id}

One customer.

Parameters

NameInTypeNotes
id requiredpathstringcus_… — any of the customer's record ids.

Answer 200

data is:

FieldTypeNotes
idstring or nullcus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id.
namestring
businessstring
phonestring
alt_phonestring
emailstring
addressstring
tagsarray of string
notesstring
record_idsarray of stringEvery one resolves to this customer.
recordsobject
records.quotesinteger
records.card_contactsinteger
records.estimates_and_invoicesinteger
records.appointmentsinteger
records.doorsintegerDoor trades only.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullLast activity or edit. ISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/customers/{id}

Edit what FieldStacker shows for the customer (like ✏️ Edit customer).

Parameters

NameInTypeNotes
id requiredpathstringcus_… — any of the customer's record ids.

Body (JSON or form-encoded)

Only the fields sent change (what FieldStacker shows for the customer; past documents keep what was on them).

FieldTypeNotes
namestringUp to 120 characters.
businessstringUp to 120 characters.
phonestringUp to 40 characters.
alt_phonestringUp to 40 characters.
emailstringUp to 160 characters.
addressstringUp to 300 characters.
notesstringUp to 4000 characters.
tagsstring or array of string

Answer 200

data is:

FieldTypeNotes
idstring or nullcus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id.
namestring
businessstring
phonestring
alt_phonestring
emailstring
addressstring
tagsarray of string
notesstring
record_idsarray of stringEvery one resolves to this customer.
recordsobject
records.quotesinteger
records.card_contactsinteger
records.estimates_and_invoicesinteger
records.appointmentsinteger
records.doorsintegerDoor trades only.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullLast activity or edit. ISO 8601 with the business's UTC offset.

Errors: 404 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

Leads · 4 routes
GETv1/leads

Leads created since a time (a bare JSON array, newest first — the polling shape).

Parameters

NameInTypeNotes
sincequerystringDefault 7 days ago. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100 (default 100). 1 to 100.

Answer 200

A JSON array of:

FieldTypeNotes
idinteger
rep_idinteger or nullThe rep the pin belongs to (assigned rep, else its owner).
rep_namestring
rep_emailstring
lead_idintegerSame as id.
lead_namestring
lead_business_namestringSet when the pin is a business.
lead_contact_namestring
lead_contact_positionstring
lead_phonestring
lead_emailstring
lead_addressstring
lead_latnumber or null
lead_lngnumber or null
lead_statusstringThe disposition key.
lead_status_labelstringThe business's own label for it.
lead_notesstringCut to 300 characters.
lead_sourcestringmap, import, api, appointment, …
lead_categorystring
lead_is_businessboolean
lead_created_atstringISO 8601 UTC ("…Z").
lead_urlstringOpens the pin in FieldStacker.

Errors: 422 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/leads

Add a lead (a pin on the map). Fires lead.created unless notify_webhooks is false.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

Send at least one of name, business_name, phone, email, address — and an address or lat + lng.

FieldTypeNotes
namestringUp to 120 characters.
business_namestringMakes the pin a business. Up to 120 characters.
contact_namestringUp to 120 characters.
positionstringUp to 80 characters.
phonestringUp to 40 characters.
emailemailUp to 255 characters.
addressstringGeocoded when lat/lng are not sent (cache first, then one lookup under the owner's daily cap). Up to 255 characters.
latnumber-90 to 90.
lngnumber-180 to 180.
notesstringUp to 500 characters.
categorystringUp to 60 characters.
statusstringA disposition key or label (default lead).
assign_toemailA teammate's email.
external_idstringYour id: the same one again returns the first lead with duplicate: true. Up to 100 characters.
notify_webhooksbooleanfalse = do not fire lead.created (avoids loops). Default true.

Answer 200

FieldTypeNotes
oktrue
duplicatebooleanPresent (true) when external_id / Idempotency-Key matched an existing lead.
leadobjectA lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape).
lead.idinteger
lead.rep_idinteger or nullThe rep the pin belongs to (assigned rep, else its owner).
lead.rep_namestring
lead.rep_emailstring
lead.lead_idintegerSame as id.
lead.lead_namestring
lead.lead_business_namestringSet when the pin is a business.
lead.lead_contact_namestring
lead.lead_contact_positionstring
lead.lead_phonestring
lead.lead_emailstring
lead.lead_addressstring
lead.lead_latnumber or null
lead.lead_lngnumber or null
lead.lead_statusstringThe disposition key.
lead.lead_status_labelstringThe business's own label for it.
lead.lead_notesstringCut to 300 characters.
lead.lead_sourcestringmap, import, api, appointment, …
lead.lead_categorystring
lead.lead_is_businessboolean
lead.lead_created_atstringISO 8601 UTC ("…Z").
lead.lead_urlstringOpens the pin in FieldStacker.

Errors: 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/leads/{id}

One lead.

Parameters

NameInTypeNotes
id requiredpathintegerLead id.

Answer 200

FieldTypeNotes
oktrue
leadobjectA lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape).
lead.idinteger
lead.rep_idinteger or nullThe rep the pin belongs to (assigned rep, else its owner).
lead.rep_namestring
lead.rep_emailstring
lead.lead_idintegerSame as id.
lead.lead_namestring
lead.lead_business_namestringSet when the pin is a business.
lead.lead_contact_namestring
lead.lead_contact_positionstring
lead.lead_phonestring
lead.lead_emailstring
lead.lead_addressstring
lead.lead_latnumber or null
lead.lead_lngnumber or null
lead.lead_statusstringThe disposition key.
lead.lead_status_labelstringThe business's own label for it.
lead.lead_notesstringCut to 300 characters.
lead.lead_sourcestringmap, import, api, appointment, …
lead.lead_categorystring
lead.lead_is_businessboolean
lead.lead_created_atstringISO 8601 UTC ("…Z").
lead.lead_urlstringOpens the pin in FieldStacker.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/leads/{id}

Change status and/or notes. Fires lead.status_changed (+ lead.sold / appointment.created).

Parameters

NameInTypeNotes
id requiredpathintegerLead id.

Body (JSON or form-encoded)

FieldTypeNotes
statusstringA disposition key or label.
notesstringReplaces the notes. Up to 500 characters.
append_notestringAdds a line.

Answer 200

FieldTypeNotes
oktrue
leadobjectA lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape).
lead.idinteger
lead.rep_idinteger or nullThe rep the pin belongs to (assigned rep, else its owner).
lead.rep_namestring
lead.rep_emailstring
lead.lead_idintegerSame as id.
lead.lead_namestring
lead.lead_business_namestringSet when the pin is a business.
lead.lead_contact_namestring
lead.lead_contact_positionstring
lead.lead_phonestring
lead.lead_emailstring
lead.lead_addressstring
lead.lead_latnumber or null
lead.lead_lngnumber or null
lead.lead_statusstringThe disposition key.
lead.lead_status_labelstringThe business's own label for it.
lead.lead_notesstringCut to 300 characters.
lead.lead_sourcestringmap, import, api, appointment, …
lead.lead_categorystring
lead.lead_is_businessboolean
lead.lead_created_atstringISO 8601 UTC ("…Z").
lead.lead_urlstringOpens the pin in FieldStacker.

Errors: 404 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

Estimates · 5 routes
GETv1/estimates

Estimates, most recently changed first (read-only). Plan: business_suite.

Parameters

NameInTypeNotes
statusquerystringdraft, sent or approved.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, sent, approved.
approved_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/estimates

Make a draft estimate for a customer, the way the app starts one (numbered, your usual tax rate, owned by the business owner). Plan: business_suite.

sj_from_start() makes it; each line goes through sidejob_item_add() (+ your automatic line rules). All in one transaction: a refused line creates nothing. Nothing is sent to anyone.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

customer_id and / or customer (at least a name, email or phone). notes, tax_rate, labor, status, dates, number and every payment field are 422 field_not_writable.

FieldTypeNotes
customer_idstringAn existing customer (cus_…): its name, phone, email and address are filled in.
customerobjectFields sent here win over the customer_id ones.
customer.namestringUp to 160 characters.
customer.phonestringUp to 40 characters.
customer.emailemailUp to 160 characters.
customer.addressstringUp to 300 characters.
customer.contact_namestringUp to 120 characters.
linesarray of objectDiscounts are added after the other lines. Your automatic line rules add their lines too, as in the app.
lines[].typestringOne of: item, discount. Default "item".
lines[].product_idintegerA price-list item: its name and list price unless you send your own (its list cost goes on the line for job costing, never returned).
lines[].namestringRequired on a line of your own. Default "Discount" on a discount. Up to 160 characters.
lines[].quantitystring or numberMore than 0, default 1. A JSON number or a decimal string, at most 2 decimal places.
lines[].unit_pricestring or numberRequired on a line of your own. A JSON number or a decimal string, at most 2 decimal places.
lines[].amountstring or numberDiscount lines only: the dollars off the document (more than 0). A JSON number or a decimal string, at most 2 decimal places.

Answer 201

data is:

FieldTypeNotes
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, sent, approved.
approved_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/estimates/{id}

One estimate.

Parameters

NameInTypeNotes
id requiredpathintegerEstimate id.

Answer 200

data is:

FieldTypeNotes
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, sent, approved.
approved_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/estimates/{id}

Replace a DRAFT estimate's customer lines (a sent, approved, in-progress or paid one is 409 not_draft).

Parameters

NameInTypeNotes
id requiredpathintegerEstimate id.

Body (JSON or form-encoded)

Only a draft can be changed (else 409 not_draft). Any other field is 422 field_not_writable / unknown_field.

FieldTypeNotes
lines requiredarray of objectThe WHOLE new list of the customer's lines (hidden lines are never touched).
lines[].typestringOne of: item, discount. Default "item".
lines[].product_idintegerA price-list item: its name and list price unless you send your own (its list cost goes on the line for job costing, never returned).
lines[].namestringRequired on a line of your own. Default "Discount" on a discount. Up to 160 characters.
lines[].quantitystring or numberMore than 0, default 1. A JSON number or a decimal string, at most 2 decimal places.
lines[].unit_pricestring or numberRequired on a line of your own. A JSON number or a decimal string, at most 2 decimal places.
lines[].amountstring or numberDiscount lines only: the dollars off the document (more than 0). A JSON number or a decimal string, at most 2 decimal places.

Answer 200

data is:

FieldTypeNotes
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, sent, approved.
approved_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/estimates/{id}/send

⚠️ Emails the estimate to its customer through the app's own send (letterhead email from your business, approve link). No body.

Emailed in the last 10 minutes is 409 recently_sent; no lines yet is 422 no_total. Send an Idempotency-Key so a retry never emails twice.

Parameters

NameInTypeNotes
id requiredpathintegerEstimate id.
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Answer 200

data is:

FieldTypeNotes
sent (next to data)object
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, sent, approved.
approved_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 502 · 403 (plus 401, 429, 500 on every route). What they mean.

Invoices · 5 routes
GETv1/invoices

Invoices, most recently changed first (read-only). Plan: business_suite.

Parameters

NameInTypeNotes
statusquerystringdraft, in_progress, sent, unpaid or paid.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, in_progress, sent, unpaid, paid.
invoice_datedate or null
due_datedate or null
amount_paidmoney string
deposit_paidmoney string
paid_atdate-time or nullISO 8601 with the business's UTC offset.
payment_methodstring or nulle.g. Cash, Card.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/invoices

Make a draft invoice for a customer, the way the app starts one (numbered, your usual tax rate, owned by the business owner). Plan: business_suite.

sj_from_start() makes it; each line goes through sidejob_item_add() (+ your automatic line rules). All in one transaction: a refused line creates nothing. Nothing is sent to anyone.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

customer_id and / or customer (at least a name, email or phone). notes, tax_rate, labor, status, dates, number and every payment field are 422 field_not_writable.

FieldTypeNotes
customer_idstringAn existing customer (cus_…): its name, phone, email and address are filled in.
customerobjectFields sent here win over the customer_id ones.
customer.namestringUp to 160 characters.
customer.phonestringUp to 40 characters.
customer.emailemailUp to 160 characters.
customer.addressstringUp to 300 characters.
customer.contact_namestringUp to 120 characters.
linesarray of objectDiscounts are added after the other lines. Your automatic line rules add their lines too, as in the app.
lines[].typestringOne of: item, discount. Default "item".
lines[].product_idintegerA price-list item: its name and list price unless you send your own (its list cost goes on the line for job costing, never returned).
lines[].namestringRequired on a line of your own. Default "Discount" on a discount. Up to 160 characters.
lines[].quantitystring or numberMore than 0, default 1. A JSON number or a decimal string, at most 2 decimal places.
lines[].unit_pricestring or numberRequired on a line of your own. A JSON number or a decimal string, at most 2 decimal places.
lines[].amountstring or numberDiscount lines only: the dollars off the document (more than 0). A JSON number or a decimal string, at most 2 decimal places.

Answer 201

data is:

FieldTypeNotes
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, in_progress, sent, unpaid, paid.
invoice_datedate or null
due_datedate or null
amount_paidmoney string
deposit_paidmoney string
paid_atdate-time or nullISO 8601 with the business's UTC offset.
payment_methodstring or nulle.g. Cash, Card.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/invoices/{id}

One invoice.

Parameters

NameInTypeNotes
id requiredpathintegerInvoice id.

Answer 200

data is:

FieldTypeNotes
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, in_progress, sent, unpaid, paid.
invoice_datedate or null
due_datedate or null
amount_paidmoney string
deposit_paidmoney string
paid_atdate-time or nullISO 8601 with the business's UTC offset.
payment_methodstring or nulle.g. Cash, Card.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/invoices/{id}

Replace a DRAFT invoice's customer lines (a sent, approved, in-progress or paid one is 409 not_draft).

Parameters

NameInTypeNotes
id requiredpathintegerInvoice id.

Body (JSON or form-encoded)

Only a draft can be changed (else 409 not_draft). Any other field is 422 field_not_writable / unknown_field.

FieldTypeNotes
lines requiredarray of objectThe WHOLE new list of the customer's lines (hidden lines are never touched).
lines[].typestringOne of: item, discount. Default "item".
lines[].product_idintegerA price-list item: its name and list price unless you send your own (its list cost goes on the line for job costing, never returned).
lines[].namestringRequired on a line of your own. Default "Discount" on a discount. Up to 160 characters.
lines[].quantitystring or numberMore than 0, default 1. A JSON number or a decimal string, at most 2 decimal places.
lines[].unit_pricestring or numberRequired on a line of your own. A JSON number or a decimal string, at most 2 decimal places.
lines[].amountstring or numberDiscount lines only: the dollars off the document (more than 0). A JSON number or a decimal string, at most 2 decimal places.

Answer 200

data is:

FieldTypeNotes
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, in_progress, sent, unpaid, paid.
invoice_datedate or null
due_datedate or null
amount_paidmoney string
deposit_paidmoney string
paid_atdate-time or nullISO 8601 with the business's UTC offset.
payment_methodstring or nulle.g. Cash, Card.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/invoices/{id}/send

⚠️ Emails the invoice to its customer through the app's own send (letterhead email from your business, pay link when card or PayPal is on). No body.

As in the app, a sent invoice is marked finished (status unpaid). It is never marked paid. A paid invoice is 409 already_paid (no receipts from the API); emailed in the last 10 minutes is 409 recently_sent. Send an Idempotency-Key so a retry never emails twice.

Parameters

NameInTypeNotes
id requiredpathintegerInvoice id.
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Answer 200

data is:

FieldTypeNotes
sent (next to data)object
linesarray of object
lines[].idinteger or nullThe line's id.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
idinteger
numberstring
typestringOne of: estimate, invoice.
customerobject
customer.idstring or nullcus_s<id>: the customer this document belongs to (GET v1/customers/{id}).
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
subtotalmoney string
labormoney string
tax_ratestringPercent, e.g. "8.25".
taxmoney string
totalmoney string
notesstring
customer_urlstring or nullThe document's existing share link (never created by the API).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
statusstringOne of: draft, in_progress, sent, unpaid, paid.
invoice_datedate or null
due_datedate or null
amount_paidmoney string
deposit_paidmoney string
paid_atdate-time or nullISO 8601 with the business's UTC offset.
payment_methodstring or nulle.g. Cash, Card.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 502 · 403 (plus 401, 429, 500 on every route). What they mean.

Payments · 2 routes
GETv1/payments

Money received, newest first (read-only). Plan: business_suite.

Parameters

NameInTypeNotes
sincequerystringPaid at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
untilquerystringPaid before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
typequerystringpayment or deposit. One of: payment, deposit.
methodquerystringOne method. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other.
invoice_idqueryintegerOnly this invoice's payments and deposits.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringpay_s<invoice> · dep_s<invoice> · dep_c<change order> · pay_q<quote> (card) · pay_k<quote> (recorded by hand) · pay_v<recurring visit>.
typestringOne of: payment, deposit.
amountmoney stringAn invoice payment is the bill less every deposit listed, so its payments add up to amount_paid.
currencystring
methodstring or nullnull on an old row with none recorded. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other, null.
method_labelstring or null
card_brandstring or nullOnly when FieldStacker stored it.
card_last4string or nullOnly when FieldStacker stored it.
statusstringOne of: succeeded, refunded, disputed.
paid_atdate-time or nullISO 8601 with the business's UTC offset.
invoice_idinteger or null
estimate_idinteger or null
change_orderinteger or nullThe change order's number (its deposit).
quote_idinteger or null
customer_idstring or null
sourcestringOne of: invoice, invoice_deposit, change_order_deposit, quote, recurring_plan.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/payments/{id}

One payment.

Parameters

NameInTypeNotes
id requiredpathstringpay_s…, dep_s…, dep_c…, pay_q…, pay_k… or pay_v….

Answer 200

data is:

FieldTypeNotes
idstringpay_s<invoice> · dep_s<invoice> · dep_c<change order> · pay_q<quote> (card) · pay_k<quote> (recorded by hand) · pay_v<recurring visit>.
typestringOne of: payment, deposit.
amountmoney stringAn invoice payment is the bill less every deposit listed, so its payments add up to amount_paid.
currencystring
methodstring or nullnull on an old row with none recorded. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other, null.
method_labelstring or null
card_brandstring or nullOnly when FieldStacker stored it.
card_last4string or nullOnly when FieldStacker stored it.
statusstringOne of: succeeded, refunded, disputed.
paid_atdate-time or nullISO 8601 with the business's UTC offset.
invoice_idinteger or null
estimate_idinteger or null
change_orderinteger or nullThe change order's number (its deposit).
quote_idinteger or null
customer_idstring or null
sourcestringOne of: invoice, invoice_deposit, change_order_deposit, quote, recurring_plan.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Quotes · 2 routes
GETv1/quotes

Quote-builder quotes, newest first (read-only). Plan: quoting.

Parameters

NameInTypeNotes
statusquerystringOne status. One of: quoted, viewed, expired, approved, install_pending, installed, declined.
sincequerystringCreated at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
untilquerystringCreated before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
typestringOne of: install, fiber, cleaning, service.
statusstringOne of: quoted, viewed, expired, approved, install_pending, installed, declined.
customerobject
customer.idstring or nullcus_q<id>, null when the quote has no contact.
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
planobject
plan.namestring
plan.term_monthsinteger or null
plan.monthlymoney string or null
plan.upfrontmoney string or null
plan.total_valuemoney string or null
priceobject or nullThe quote's money in its own terms; null when nothing was priced.
price.amountmoney string
price.perstring or nullOne of: month, visit, null.
price.cadencestring or null
frequencystringone_time, or a recurring clean's frequency.
currencystring
linesarray of objectThe customer-facing breakdown the quote showed.
lines[].namestring
lines[].detailstring
lines[].pricemoney string
lines[].includedboolean
lines[].monthlymoney stringPresent on a monthly line.
equipmentarray of object
equipment[].keystring
equipment[].namestring
equipment[].quantityinteger1 to 999.
invoice_idinteger or nullThe invoice the quote is billed through.
created_atdate-time or nullISO 8601 with the business's UTC offset.
viewed_atdate-time or nullISO 8601 with the business's UTC offset.
expires_atdate-time or nullISO 8601 with the business's UTC offset.
approved_atdate-time or nullISO 8601 with the business's UTC offset.
installed_ondate or null
install_verifiedboolean
declined_ondate or null

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/quotes/{id}

One quote.

Parameters

NameInTypeNotes
id requiredpathintegerQuote id.

Answer 200

data is:

FieldTypeNotes
idinteger
typestringOne of: install, fiber, cleaning, service.
statusstringOne of: quoted, viewed, expired, approved, install_pending, installed, declined.
customerobject
customer.idstring or nullcus_q<id>, null when the quote has no contact.
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
planobject
plan.namestring
plan.term_monthsinteger or null
plan.monthlymoney string or null
plan.upfrontmoney string or null
plan.total_valuemoney string or null
priceobject or nullThe quote's money in its own terms; null when nothing was priced.
price.amountmoney string
price.perstring or nullOne of: month, visit, null.
price.cadencestring or null
frequencystringone_time, or a recurring clean's frequency.
currencystring
linesarray of objectThe customer-facing breakdown the quote showed.
lines[].namestring
lines[].detailstring
lines[].pricemoney string
lines[].includedboolean
lines[].monthlymoney stringPresent on a monthly line.
equipmentarray of object
equipment[].keystring
equipment[].namestring
equipment[].quantityinteger1 to 999.
invoice_idinteger or nullThe invoice the quote is billed through.
created_atdate-time or nullISO 8601 with the business's UTC offset.
viewed_atdate-time or nullISO 8601 with the business's UTC offset.
expires_atdate-time or nullISO 8601 with the business's UTC offset.
approved_atdate-time or nullISO 8601 with the business's UTC offset.
installed_ondate or null
install_verifiedboolean
declined_ondate or null

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Products · 2 routes
GETv1/products

The price list, by id (read-only). Plan: quoting or business_suite.

Parameters

NameInTypeNotes
qquerystringName or SKU contains. Up to 100 characters.
categoryquerystringExact category (any case). Up to 40 characters.
activequerystringtrue (default), false (hidden items) or all. One of: true, false, all. Default "true".
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
keystringThe price-list key.
namestring
skustringThe model / part number.
categorystring
pricemoney stringThe customer price.
currencystring
unitstringAlways each: every line is quantity × price. One of: each.
taxabletrueAlways true: an estimate or invoice charges its tax rate on every line.
activebooleanfalse = a hidden item.
shown_on_websiteboolean

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/products/{id}

One product (a hidden one answers with active: false).

Parameters

NameInTypeNotes
id requiredpathintegerProduct id.

Answer 200

data is:

FieldTypeNotes
idinteger
keystringThe price-list key.
namestring
skustringThe model / part number.
categorystring
pricemoney stringThe customer price.
currencystring
unitstringAlways each: every line is quantity × price. One of: each.
taxabletrueAlways true: an estimate or invoice charges its tax rate on every line.
activebooleanfalse = a hidden item.
shown_on_websiteboolean

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Services (price list) · 1 route
GETv1/services

The price list (not hidden items). Plan: quoting or business_suite.

Parameters

NameInTypeNotes
qquerystringName or model contains. Up to 100 characters.
categoryquerystringExact category (any case). Up to 40 characters.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
keystring
namestring
modelstring
categorystring
pricemoney string
currencystring
shown_on_websiteboolean

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

Form leads · 2 routes
GETv1/form-leads

Website, landing-page and card form leads, newest first (read-only).

Parameters

NameInTypeNotes
sincequerystringCreated after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
sourcequerystringwebsite_message, website_form, landing_page or card. One of: website_message, website_form, landing_page, card.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringmsg_<id> | lp_<id> | card_<id>
sourcestringOne of: website_message, website_form, landing_page, card.
namestring
emailstring
phonestring
addressstring
messagestring
campaign_idinteger or null
customer_idstring or nullThe customer the lead became (null for website messages).
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/form-leads/{id}

One form lead.

Parameters

NameInTypeNotes
id requiredpathstringmsg_<id>, lp_<id> or card_<id>.

Answer 200

data is:

FieldTypeNotes
idstringmsg_<id> | lp_<id> | card_<id>
sourcestringOne of: website_message, website_form, landing_page, card.
namestring
emailstring
phonestring
addressstring
messagestring
campaign_idinteger or null
customer_idstring or nullThe customer the lead became (null for website messages).
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Jobs & team

Bookings · 4 routes
GETv1/bookings

Online bookings and schedule appointments, by start time (oldest first).

Parameters

NameInTypeNotes
fromquerystringStart at or after (default 30 days ago). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringStart before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
statusquerystringComma-separated: pending, confirmed, canceled, declined, completed, no_show.
typequerystringonline_booking or appointment. One of: online_booking, appointment.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringbk_<id> online booking, ap_<id> schedule appointment.
typestringOne of: online_booking, appointment.
statusstringOne of: pending, confirmed, canceled, declined, completed, no_show.
titlestring
servicestring or nullThe booking page service key (online bookings only).
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
notesstring
job_numberstring or nullSchedule appointments only.
changeablebooleantrue = PATCH may move / cancel it.
created_atdate-time or nullISO 8601 with the business's UTC offset.
confirmed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/bookings

Book an open time through the booking page's own path (the owner is notified; the customer gets the same email / text as a website booking).

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

The customer fields may also be sent flat (name, email, phone, address, notes, answers). Name plus an email or phone is required.

FieldTypeNotes
start_at requireddate-timeOne of the open times from GET v1/availability (a date alone is refused).
servicestringA service key from GET v1/availability. Up to 60 characters.
customer requiredobject
customer.name requiredstringUp to 120 characters.
customer.emailemailUp to 160 characters.
customer.phonestringUp to 40 characters.
customer.addressstringUp to 300 characters.
customer.notesstringUp to 1000 characters.
customer.answersobjectIntake question key → answer (GET v1/availability lists the questions).

Answer 201

data is:

FieldTypeNotes
needs_approval (next to data)boolean
idstringbk_<id> online booking, ap_<id> schedule appointment.
typestringOne of: online_booking, appointment.
statusstringOne of: pending, confirmed, canceled, declined, completed, no_show.
titlestring
servicestring or nullThe booking page service key (online bookings only).
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
notesstring
job_numberstring or nullSchedule appointments only.
changeablebooleantrue = PATCH may move / cancel it.
created_atdate-time or nullISO 8601 with the business's UTC offset.
confirmed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/bookings/{id}

One booking.

Parameters

NameInTypeNotes
id requiredpathstringbk_<id> or ap_<id>.

Answer 200

data is:

FieldTypeNotes
idstringbk_<id> online booking, ap_<id> schedule appointment.
typestringOne of: online_booking, appointment.
statusstringOne of: pending, confirmed, canceled, declined, completed, no_show.
titlestring
servicestring or nullThe booking page service key (online bookings only).
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
notesstring
job_numberstring or nullSchedule appointments only.
changeablebooleantrue = PATCH may move / cancel it.
created_atdate-time or nullISO 8601 with the business's UTC offset.
confirmed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/bookings/{id}

Move, cancel or confirm an online booking (website service bookings: move or cancel).

Parameters

NameInTypeNotes
id requiredpathstringbk_<id> or ap_<id>.

Body (JSON or form-encoded)

Send start_at or status (not start_at with canceled).

FieldTypeNotes
start_atdate-timeMove it (an open time).
statusstringOne of: canceled, confirmed.

Answer 200

data is:

FieldTypeNotes
idstringbk_<id> online booking, ap_<id> schedule appointment.
typestringOne of: online_booking, appointment.
statusstringOne of: pending, confirmed, canceled, declined, completed, no_show.
titlestring
servicestring or nullThe booking page service key (online bookings only).
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
notesstring
job_numberstring or nullSchedule appointments only.
changeablebooleantrue = PATCH may move / cancel it.
created_atdate-time or nullISO 8601 with the business's UTC offset.
confirmed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

Availability · 1 route
GETv1/availability

Open booking times, the services and the intake questions.

Parameters

NameInTypeNotes
datequerydateYYYY-MM-DD: the open start times that day. Without it: the next dates with any.
servicequerystringA service key.

Answer 200

data is:

FieldTypeNotes
enabledbooleanOnline booking is on.
needs_approvalboolean
timezonestring
duration_mininteger
servicesarray of object
services[].keystring
services[].labelstring
questionsarray of object
questions[].keystring
questions[].labelstring
questions[].typestring
questions[].requiredboolean
questions[].optionsarray of string
datesarray of dateWithout ?date: the next days with an open time (≤30).
datedateWith ?date.
slotsarray of objectWith ?date: the open start times.
slots[].start_atdate-time or nullISO 8601 with the business's UTC offset.
slots[].end_atdate-time or nullISO 8601 with the business's UTC offset.
slots[].labelstring

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

Schedule · 1 route
GETv1/schedule

One calendar feed (jobs, appointments, online bookings, recurring-clean visits awaiting a date), by start time. Plan: the Schedule board or business_suite.

Parameters

NameInTypeNotes
fromquerystringDefault: today. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringDefault: from + 7 days (a date = the end of that day). At most 92 days. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
tech_idqueryintegerA person id from GET v1/team.
kindquerystringComma-separated: job, appointment, online_booking, plan_visit.
include_canceledquerystringtrue adds canceled and declined entries. One of: true, false.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringap_<id>, bk_<id> or cpv_<id>: the record's own id.
kindstringOne of: job, appointment, online_booking, plan_visit.
job_typestring or nullOne of: install, service_booking, service_call, plan_visit, job, null.
statusstringA job's board status, a booking's status, or awaiting_confirmation for a plan visit.
titlestring
start_atdate-time or nullnull on an all-day plan visit. ISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
datestringYYYY-MM-DD.
all_dayboolean
timezonestring
personobject or null
person.idinteger
person.namestring
customerobject
customer.namestring
customer.addressstring
resourcestringThe full record: v1/jobs/…, v1/bookings/… or v1/service-plans/cp_….

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

Jobs · 5 routes
GETv1/jobs

Jobs on the schedule, most recently changed first. Plan: business_suite.

Parameters

NameInTypeNotes
statusquerystringComma-separated: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
typequerystringComma-separated: install, service_booking, service_call, plan_visit, job.
fromquerystringStart at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringStart before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
tech_idqueryintegerA person id from GET v1/team.
customer_idquerystringA cus_… id from GET v1/customers: every job on that customer's records.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringap_<id>: the same id GET v1/bookings uses.
typestringOne of: install, service_booking, service_call, plan_visit, job.
statusstringThe Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
titlestring
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
arrival_window_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
assigned_toobject or nullWho does it.
assigned_to.idinteger
assigned_to.namestring
offer_statusstring or nullThe tech's answer to the offer. One of: pending, accepted, declined, null.
booked_byobject
booked_by.idinteger
booked_by.namestring
job_numberstring or null
notesstring
invoice_idinteger or nullGET v1/invoices/{id}.
estimate_idinteger or nullGET v1/estimates/{id}.
quote_idinteger or null
plan_idstring or nullsp_<id> or cp_<id> on a plan visit.
currencystring
linesarray of objectThe linked invoice's (or estimate's) customer lines. Never a hidden line.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
equipmentarray of objectWhat goes in: names and quantities only.
equipment[].namestring
equipment[].quantitystringDecimal string without trailing zeros ("2", "2.5").
en_route_atdate-time or nullISO 8601 with the business's UTC offset.
checked_in_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullThe last change (a move, an offer answered, on the way, check-in, finish, cancel). ISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/jobs

Dispatch an install request to a tech (the app's Dispatch). The tech is notified in the app and by email and accepts or sends it back. Plan: business_suite + dispatch.

create_install_request(): the tech's hours and calendar are checked (409 tech_unavailable) and their stock (409 insufficient_stock; resend with "force": true). A job text goes out only when the business's job-assignment text template is approved and the tech opted in. Tech pay is never set.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

A bonus field is refused (422 not_supported): tech pay is set in FieldStacker.

FieldTypeNotes
tech_id requiredintegerA person with assignable: true.
start_at requireddate-timeDate AND time, in the future (read in the tech's own zone).
arrival_windowbooleantrue = a 2-hour arrival window from start_at. Default false.
customerobjectMay also be sent flat.
customer.name requiredstringUp to 200 characters.
customer.phonestringUp to 40 characters.
customer.emailemailUp to 160 characters.
customer.addressstringUp to 500 characters.
customer.latnumber-90 to 90.
customer.lngnumber-180 to 180.
items requiredarray of object
items[].name requiredstringUp to 160 characters.
items[].quantityintegerDefault 1. 1 to 999.
items[].keystringYour catalog key. Up to 60 characters.
job_numberstringUp to 60 characters.
forcebooleantrue = dispatch even if the tech is short of tracked equipment. Default false.

Answer 201

data is:

FieldTypeNotes
idstringap_<id>: the same id GET v1/bookings uses.
typestringOne of: install, service_booking, service_call, plan_visit, job.
statusstringThe Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
titlestring
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
arrival_window_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
assigned_toobject or nullWho does it.
assigned_to.idinteger
assigned_to.namestring
offer_statusstring or nullThe tech's answer to the offer. One of: pending, accepted, declined, null.
booked_byobject
booked_by.idinteger
booked_by.namestring
job_numberstring or null
notesstring
invoice_idinteger or nullGET v1/invoices/{id}.
estimate_idinteger or nullGET v1/estimates/{id}.
quote_idinteger or null
plan_idstring or nullsp_<id> or cp_<id> on a plan visit.
currencystring
linesarray of objectThe linked invoice's (or estimate's) customer lines. Never a hidden line.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
equipmentarray of objectWhat goes in: names and quantities only.
equipment[].namestring
equipment[].quantitystringDecimal string without trailing zeros ("2", "2.5").
en_route_atdate-time or nullISO 8601 with the business's UTC offset.
checked_in_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullThe last change (a move, an offer answered, on the way, check-in, finish, cancel). ISO 8601 with the business's UTC offset.

Errors: 400 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/jobs/{id}

One job (a sales consult's id is 404 here: it is a booking).

Parameters

NameInTypeNotes
id requiredpathstringap_<id>.

Answer 200

data is:

FieldTypeNotes
idstringap_<id>: the same id GET v1/bookings uses.
typestringOne of: install, service_booking, service_call, plan_visit, job.
statusstringThe Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
titlestring
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
arrival_window_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
assigned_toobject or nullWho does it.
assigned_to.idinteger
assigned_to.namestring
offer_statusstring or nullThe tech's answer to the offer. One of: pending, accepted, declined, null.
booked_byobject
booked_by.idinteger
booked_by.namestring
job_numberstring or null
notesstring
invoice_idinteger or nullGET v1/invoices/{id}.
estimate_idinteger or nullGET v1/estimates/{id}.
quote_idinteger or null
plan_idstring or nullsp_<id> or cp_<id> on a plan visit.
currencystring
linesarray of objectThe linked invoice's (or estimate's) customer lines. Never a hidden line.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
equipmentarray of objectWhat goes in: names and quantities only.
equipment[].namestring
equipment[].quantitystringDecimal string without trailing zeros ("2", "2.5").
en_route_atdate-time or nullISO 8601 with the business's UTC offset.
checked_in_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullThe last change (a move, an offer answered, on the way, check-in, finish, cancel). ISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/jobs/{id}

Move a job: a sold-deal install (its tech is offered the new time) or a customer's own service booking (customer-portal rules, the customer is emailed).

Any other job, and a completed or canceled one, is 409 not_changeable: move it on the Schedule board in FieldStacker.

Parameters

NameInTypeNotes
id requiredpathstringap_<id>.

Body (JSON or form-encoded)

FieldTypeNotes
start_at requireddate-timeDate and time, in the future.
arrival_windowbooleanA sold install only.

Answer 200

data is:

FieldTypeNotes
idstringap_<id>: the same id GET v1/bookings uses.
typestringOne of: install, service_booking, service_call, plan_visit, job.
statusstringThe Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
titlestring
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
arrival_window_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
assigned_toobject or nullWho does it.
assigned_to.idinteger
assigned_to.namestring
offer_statusstring or nullThe tech's answer to the offer. One of: pending, accepted, declined, null.
booked_byobject
booked_by.idinteger
booked_by.namestring
job_numberstring or null
notesstring
invoice_idinteger or nullGET v1/invoices/{id}.
estimate_idinteger or nullGET v1/estimates/{id}.
quote_idinteger or null
plan_idstring or nullsp_<id> or cp_<id> on a plan visit.
currencystring
linesarray of objectThe linked invoice's (or estimate's) customer lines. Never a hidden line.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
equipmentarray of objectWhat goes in: names and quantities only.
equipment[].namestring
equipment[].quantitystringDecimal string without trailing zeros ("2", "2.5").
en_route_atdate-time or nullISO 8601 with the business's UTC offset.
checked_in_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullThe last change (a move, an offer answered, on the way, check-in, finish, cancel). ISO 8601 with the business's UTC offset.

Errors: 404 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/jobs/{id}/assign

Hand a sold install, or an install request its tech sent back, to another tech (offered to them). Plan: business_suite + dispatch.

Anything else is 409 not_changeable (reassign it on the Schedule board). The same tech again is 409 already_assigned. Tech pay is unchanged.

Parameters

NameInTypeNotes
id requiredpathstringap_<id>.
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

FieldTypeNotes
tech_id requiredintegerA tech the job's seller can hand work to.

Answer 200

data is:

FieldTypeNotes
idstringap_<id>: the same id GET v1/bookings uses.
typestringOne of: install, service_booking, service_call, plan_visit, job.
statusstringThe Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule.
titlestring
start_atdate-time or nullISO 8601 with the business's UTC offset.
end_atdate-time or nullISO 8601 with the business's UTC offset.
duration_mininteger or null
arrival_window_mininteger or null
timezonestring
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
assigned_toobject or nullWho does it.
assigned_to.idinteger
assigned_to.namestring
offer_statusstring or nullThe tech's answer to the offer. One of: pending, accepted, declined, null.
booked_byobject
booked_by.idinteger
booked_by.namestring
job_numberstring or null
notesstring
invoice_idinteger or nullGET v1/invoices/{id}.
estimate_idinteger or nullGET v1/estimates/{id}.
quote_idinteger or null
plan_idstring or nullsp_<id> or cp_<id> on a plan visit.
currencystring
linesarray of objectThe linked invoice's (or estimate's) customer lines. Never a hidden line.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
equipmentarray of objectWhat goes in: names and quantities only.
equipment[].namestring
equipment[].quantitystringDecimal string without trailing zeros ("2", "2.5").
en_route_atdate-time or nullISO 8601 with the business's UTC offset.
checked_in_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullThe last change (a move, an offer answered, on the way, check-in, finish, cancel). ISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

Team · 2 routes
GETv1/team

The business's people, by id (read-only). No plan gate.

Parameters

NameInTypeNotes
rolequerystringComma-separated: owner, sales, tech, sales_tech.
relationshipquerystringComma-separated: owner, team_member, seat, crew.
assignablequerystringOnly people a job can (true) or cannot (false) be handed to. One of: true, false.
activequerystringActive (true) or switched off (false). One of: true, false.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idintegerUse it as tech_id in jobs and the schedule.
namestring
rolestringOne of: owner, sales, tech, sales_tech.
role_labelstring
works_jobsboolean
relationshipstringOne of: owner, team_member, seat, crew.
assignablebooleanA job can be handed to them (POST v1/jobs, assign).
emailstring or nullOnly for the business's own staff; null for a tech in a dealer's shared pool.
activeboolean
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/team/{id}

One person.

Parameters

NameInTypeNotes
id requiredpathintegerPerson id.

Answer 200

data is:

FieldTypeNotes
idintegerUse it as tech_id in jobs and the schedule.
namestring
rolestringOne of: owner, sales, tech, sales_tech.
role_labelstring
works_jobsboolean
relationshipstringOne of: owner, team_member, seat, crew.
assignablebooleanA job can be handed to them (POST v1/jobs, assign).
emailstring or nullOnly for the business's own staff; null for a tech in a dealer's shared pool.
activeboolean
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Service plans · 2 routes
GETv1/service-plans

Service plans and recurring cleans, most recently changed first (read-only). Plan: business_suite.

Parameters

NameInTypeNotes
statusquerystringComma-separated: active, paused, past_due, canceled, ended.
typequerystringComma-separated: service_plan, cleaning_plan.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringsp_<id> a service plan, cp_<id> a recurring clean.
typestringOne of: service_plan, cleaning_plan.
statusstringactive, paused, canceled or ended; a recurring clean can also be past_due.
customerobject
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
scheduleobject
schedule.frequencystringw1–w4, m1, m2, m3, m6, m12 (service plan) or a clean's frequency (weekly, biweekly, monthly, …).
schedule.frequency_labelstring
schedule.labelstring
schedule.interval_daysintegerRecurring cleans only.
schedule.first_visit_ondate or null
schedule.visit_timestring or nullHH:MM.
schedule.duration_mininteger or null
schedule.endsobject
schedule.ends.modestringnever, after or on.
schedule.ends.after_visitsinteger or null
schedule.ends.ondate or null
schedule.ends.labelstring
techobject or null
tech.idinteger
tech.namestring
currencystring
price_per_visitmoney stringTax included.
tax_ratestring or nullPercent; null on a recurring clean.
linesarray of objectEmpty on a recurring clean.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
autopaybooleanAutopay on and a card saved. Never card details.
next_visit_ondate or null
visits_madeinteger
notesstring
source_invoice_idinteger or null
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/service-plans/{id}

One plan with its latest 100 visits.

Parameters

NameInTypeNotes
id requiredpathstringsp_<id> or cp_<id>.

Answer 200

data is:

FieldTypeNotes
idstringsp_<id> a service plan, cp_<id> a recurring clean.
typestringOne of: service_plan, cleaning_plan.
statusstringactive, paused, canceled or ended; a recurring clean can also be past_due.
customerobject
customer.namestring
customer.contact_namestring
customer.phonestring
customer.emailstring
customer.addressstring
scheduleobject
schedule.frequencystringw1–w4, m1, m2, m3, m6, m12 (service plan) or a clean's frequency (weekly, biweekly, monthly, …).
schedule.frequency_labelstring
schedule.labelstring
schedule.interval_daysintegerRecurring cleans only.
schedule.first_visit_ondate or null
schedule.visit_timestring or nullHH:MM.
schedule.duration_mininteger or null
schedule.endsobject
schedule.ends.modestringnever, after or on.
schedule.ends.after_visitsinteger or null
schedule.ends.ondate or null
schedule.ends.labelstring
techobject or null
tech.idinteger
tech.namestring
currencystring
price_per_visitmoney stringTax included.
tax_ratestring or nullPercent; null on a recurring clean.
linesarray of objectEmpty on a recurring clean.
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
autopaybooleanAutopay on and a card saved. Never card details.
next_visit_ondate or null
visits_madeinteger
notesstring
source_invoice_idinteger or null
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
visitsarray of objectThe latest 100, newest first.
visits[].idstring
visits[].datedate or null
visits[].statusstring
visits[].job_idstring or nullap_<id>: GET v1/jobs/{id}.
visits[].invoice_idinteger or null
visits[].completedboolean
visits[].paidboolean
visits[].amountmoney stringRecurring-clean visits only.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Monitoring accounts · 2 routes
GETv1/monitoring-accounts

The owner's alarm monitoring accounts, most recently changed first (read-only). Monitoring switched on (else 403 feature_off), then business_suite.

Parameters

NameInTypeNotes
statusquerystringComma-separated: active, paused, canceled, ended.
past_duequerystringOnly past-due (true) or not (false). One of: true, false.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstring
statusstringactive, paused, canceled or ended.
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
monthly_amountmoney stringPer month, tax included (quarterly / yearly spread over the months).
monthly_rmrmoney stringThe same before tax.
billingobject
billing.frequencystring
billing.frequency_labelstring
billing.amount_per_billmoney string
billing.next_bill_ondate or null
billing.tax_ratestring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
contractobject
contract.start_ondate or null
contract.term_monthsinteger
contract.term_end_ondate or null
contract.auto_renewboolean
contract.month_to_monthboolean
central_stationobject
central_station.namestring or null
central_station.account_numberstring or null
alarmcom_customer_idstring or null
panelstring or null
contract_numberstring or null
autopayboolean
past_duebooleanAn unpaid bill older than today.
past_due_amountmoney string
days_past_dueintegerFrom the oldest unpaid bill.
unpaid_invoicesinteger
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/monitoring-accounts/{id}

One monitoring account.

Parameters

NameInTypeNotes
id requiredpathstringma_<id>.

Answer 200

data is:

FieldTypeNotes
idstring
statusstringactive, paused, canceled or ended.
customerobject
customer.namestring
customer.phonestring
customer.emailstring
customer.addressstring
currencystring
monthly_amountmoney stringPer month, tax included (quarterly / yearly spread over the months).
monthly_rmrmoney stringThe same before tax.
billingobject
billing.frequencystring
billing.frequency_labelstring
billing.amount_per_billmoney string
billing.next_bill_ondate or null
billing.tax_ratestring
linesarray of object
lines[].namestring
lines[].quantitystringDecimal string without trailing zeros ("2", "2.5").
lines[].unit_pricemoney string
lines[].amountmoney string
lines[].discountobjectPresent only on a line with a customer-visible discount.
lines[].discount.typestringOne of: percent, amount_per_unit.
lines[].discount.valuemoney string
lines[].discount.amount_offmoney string
lines[].discount.notestring
contractobject
contract.start_ondate or null
contract.term_monthsinteger
contract.term_end_ondate or null
contract.auto_renewboolean
contract.month_to_monthboolean
central_stationobject
central_station.namestring or null
central_station.account_numberstring or null
alarmcom_customer_idstring or null
panelstring or null
contract_numberstring or null
autopayboolean
past_duebooleanAn unpaid bill older than today.
past_due_amountmoney string
days_past_dueintegerFrom the oldest unpaid bill.
unpaid_invoicesinteger
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Inventory · 4 routes
GETv1/inventory

Stock on hand per person, with the low-stock flag. Plan: inventory (tech tools).

Parameters

NameInTypeNotes
sourcequerystringinstall_inventory or sub_dealer_stock. One of: install_inventory, sub_dealer_stock.
held_byquerystringrep_<id>.
low_stockquerystringtrue = at or under its low-stock level; false = not. One of: true, false.
qquerystringName or SKU contains. Up to 100 characters.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringinv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only).
sourcestringOne of: install_inventory, sub_dealer_stock.
namestring
skustring
keystring
product_idinteger or nullThe price-list item with the same key, or null.
held_byobject
held_by.idstringrep_<id> (GET v1/team: the number after rep_).
held_by.namestring
quantity_on_handinteger
low_stock_atinteger or nullThe low-stock level, null when none is set.
is_lowbooleanA level is set and quantity_on_hand ≤ it (the app's own rule).
unit_pricemoney string
currencystring
adjustablebooleantrue = POST v1/inventory/{id}/adjust works (install inventory only).
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/inventory/{id}

One inventory item.

Parameters

NameInTypeNotes
id requiredpathstringinv_<id> or sub_<id>.

Answer 200

data is:

FieldTypeNotes
idstringinv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only).
sourcestringOne of: install_inventory, sub_dealer_stock.
namestring
skustring
keystring
product_idinteger or nullThe price-list item with the same key, or null.
held_byobject
held_by.idstringrep_<id> (GET v1/team: the number after rep_).
held_by.namestring
quantity_on_handinteger
low_stock_atinteger or nullThe low-stock level, null when none is set.
is_lowbooleanA level is set and quantity_on_hand ≤ it (the app's own rule).
unit_pricemoney string
currencystring
adjustablebooleantrue = POST v1/inventory/{id}/adjust works (install inventory only).
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/inventory/movements

The install inventory's stock in / out log, newest first.

Parameters

NameInTypeNotes
item_idquerystringinv_<id> (sub-dealer stock keeps no log).
held_byquerystringrep_<id>.
reasonquerystringOne reason. One of: receive, use, install, adjust, transfer, side_job.
sincequerystringAt or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
untilquerystringBefore (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstring
item_idstring
item_namestring
skustring
held_byobject
held_by.idstringrep_<id> (GET v1/team: the number after rep_).
held_by.namestring
quantity_changeinteger+ received, − used.
reasonstringOne of: receive, use, install, adjust, transfer, side_job.
notestring
quote_idinteger or nullSet when the stock went to a job on one of your own quotes.
customer_idstring or nullcus_q<quote id> when quote_id is set.
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/inventory/{id}/adjust

Change an install-inventory count through the app's own writer (row lock, never below 0, the movement logged).

The same as the ▲/▼ arrows and the typed count on the Inventory page. Sub-dealer stock answers 409 not_adjustable.

Parameters

NameInTypeNotes
id requiredpathstringinv_<id> (install inventory).
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

Send exactly one of quantity_change or set_quantity. Any other field is 422 unknown_field.

FieldTypeNotes
quantity_changeintegerA non-zero whole number: + received, − used. Never goes below 0. -100000 to 100000.
set_quantityintegerThe count on hand; the API records the difference. 0 to 1000000.
reasonstringDefault: receive when up, use when down. One of: receive, use, adjust.
notestringDefault "Received (API)" / "Used (API)". Up to 200 characters.

Answer 200

data is:

FieldTypeNotes
movement (next to data)object or nullThe logged movement; null when nothing changed (the count was already right, or a decrease was clamped at 0).
idstringinv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only).
sourcestringOne of: install_inventory, sub_dealer_stock.
namestring
skustring
keystring
product_idinteger or nullThe price-list item with the same key, or null.
held_byobject
held_by.idstringrep_<id> (GET v1/team: the number after rep_).
held_by.namestring
quantity_on_handinteger
low_stock_atinteger or nullThe low-stock level, null when none is set.
is_lowbooleanA level is set and quantity_on_hand ≤ it (the app's own rule).
unit_pricemoney string
currencystring
adjustablebooleantrue = POST v1/inventory/{id}/adjust works (install inventory only).
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

Records & documents

Contracts · 4 routes
GETv1/contracts

E-sign contracts, most recently changed first. Plan: business_suite or quoting.

Parameters

NameInTypeNotes
statusquerystringOne status. One of: draft, sent, viewed, signed, canceled.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
titlestring
statusstringOne of: draft, sent, viewed, signed, canceled.
templateobject
template.idinteger
template.namestring
template.versioninteger
customerobject
customer.idstring or nullcus_s… (from an estimate / invoice) or cus_q… (from a quote), else null.
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate, invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
rep_idinteger
door_to_doorboolean
cancel_bydate or nullThe cancel-by date of a door-to-door sale.
countersign_requiredboolean
countersigned_atdate-time or nullISO 8601 with the business's UTC offset.
view_countinteger
document_idinteger or nullIts Documents entry once signed.
signed_pdf_urlstring or nullSigned contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
first_viewed_atdate-time or nullISO 8601 with the business's UTC offset.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_bystring or null

Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/contracts

Make a DRAFT contract from a template and one record (the app's New contract and its auto-fill). Nothing is sent.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

template_id plus EXACTLY ONE of estimate_id, invoice_id, quote_id, monitoring_account_id, customer_id, customer. Makes a draft; nothing is sent.

FieldTypeNotes
template_id requiredintegerA current template (GET v1/contract-templates).
estimate_idinteger
invoice_idinteger
quote_idintegerAn alarm quote.
monitoring_account_idintegerThe number of an ma_<id>.
customer_idstringcus_…: fills from that customer's records.
customerobjectSomeone not in FieldStacker yet: at least a name, phone or email.
customer.namestringUp to 120 characters.
customer.phonestringUp to 40 characters.
customer.emailemailUp to 160 characters.
customer.addressstringUp to 300 characters.

Answer 201

data is:

FieldTypeNotes
idinteger
titlestring
statusstringOne of: draft, sent, viewed, signed, canceled.
templateobject
template.idinteger
template.namestring
template.versioninteger
customerobject
customer.idstring or nullcus_s… (from an estimate / invoice) or cus_q… (from a quote), else null.
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate, invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
rep_idinteger
door_to_doorboolean
cancel_bydate or nullThe cancel-by date of a door-to-door sale.
countersign_requiredboolean
countersigned_atdate-time or nullISO 8601 with the business's UTC offset.
view_countinteger
document_idinteger or nullIts Documents entry once signed.
signed_pdf_urlstring or nullSigned contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
first_viewed_atdate-time or nullISO 8601 with the business's UTC offset.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_bystring or null

Errors: 400 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/contracts/{id}

One contract.

Parameters

NameInTypeNotes
id requiredpathintegerContract id.

Answer 200

data is:

FieldTypeNotes
idinteger
titlestring
statusstringOne of: draft, sent, viewed, signed, canceled.
templateobject
template.idinteger
template.namestring
template.versioninteger
customerobject
customer.idstring or nullcus_s… (from an estimate / invoice) or cus_q… (from a quote), else null.
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate, invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
rep_idinteger
door_to_doorboolean
cancel_bydate or nullThe cancel-by date of a door-to-door sale.
countersign_requiredboolean
countersigned_atdate-time or nullISO 8601 with the business's UTC offset.
view_countinteger
document_idinteger or nullIts Documents entry once signed.
signed_pdf_urlstring or nullSigned contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
first_viewed_atdate-time or nullISO 8601 with the business's UTC offset.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_bystring or null

Errors: 404 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/contracts/{id}/send

⚠️ channel email messages your customer (the app's Email the link); channel link sends nothing and returns the signing link. Signing itself happens on the customer's own page.

Parameters

NameInTypeNotes
id requiredpathintegerContract id.
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded), optional

FieldTypeNotes
channelstringemail = ✉️ Email the link (the customer gets "Please review and sign" from your business). link = 🔗 Copy link (nothing is sent; you get sign_url). One of: email, link. Default "email".

Answer 200

data is:

FieldTypeNotes
sent (next to data)object
idinteger
titlestring
statusstringOne of: draft, sent, viewed, signed, canceled.
templateobject
template.idinteger
template.namestring
template.versioninteger
customerobject
customer.idstring or nullcus_s… (from an estimate / invoice) or cus_q… (from a quote), else null.
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate, invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
rep_idinteger
door_to_doorboolean
cancel_bydate or nullThe cancel-by date of a door-to-door sale.
countersign_requiredboolean
countersigned_atdate-time or nullISO 8601 with the business's UTC offset.
view_countinteger
document_idinteger or nullIts Documents entry once signed.
signed_pdf_urlstring or nullSigned contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
sent_atdate-time or nullISO 8601 with the business's UTC offset.
first_viewed_atdate-time or nullISO 8601 with the business's UTC offset.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_atdate-time or nullISO 8601 with the business's UTC offset.
canceled_bystring or null

Errors: 400 · 404 · 409 · 422 · 502 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

Contract templates · 1 route
GETv1/contract-templates

Your current contract templates, by id (read-only).

Parameters

NameInTypeNotes
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idintegertemplate_id for POST v1/contracts.
namestring
versioninteger
door_to_doorboolean
cancel_daysinteger
countersign_requiredboolean
fieldsarray of object
fields[].keystring
fields[].kindstring
fields[].labelstring
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

Documents · 2 routes
GETv1/documents

The Documents page (signed contracts), newest signed first (read-only).

Parameters

NameInTypeNotes
kindquerystringcontract. One of: contract.
statusquerystringsigned or canceled. One of: signed, canceled.
fromquerystringSigned at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringSigned before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
kindstringcontract.
statusstringsigned or canceled.
titlestring
template_namestring
customerobject
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate_or_invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
contract_idinteger or null
rep_idinteger
doc_hashstring or nullSHA-256 of the signed words.
download_urlstring or nullThe Documents page's own download: opens only for someone signed in to FieldStacker who may see it.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/documents/{id}

One document.

Parameters

NameInTypeNotes
id requiredpathintegerDocument id.

Answer 200

data is:

FieldTypeNotes
idinteger
kindstringcontract.
statusstringsigned or canceled.
titlestring
template_namestring
customerobject
customer.namestring
customer.emailstring
customer.phonestring
customer.addressstring
sourceobject
source.typestring or nullestimate_or_invoice, quote, monitoring_account, customer or blank.
source.idinteger or null
contract_idinteger or null
rep_idinteger
doc_hashstring or nullSHA-256 of the signed words.
download_urlstring or nullThe Documents page's own download: opens only for someone signed in to FieldStacker who may see it.
signed_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Notes · 2 routes
GETv1/notes

A lead's or a customer's notes (send lead_id OR customer_id). Customer notes need a plan with Customers.

Parameters

NameInTypeNotes
lead_idqueryintegerA lead (pin) id.
customer_idquerystringA cus_… id.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
subject (next to data)object
idstringln_<id> a lead's History entry, cn_<id> a customer's note.
typestringOne of: note, voice_note.
textstring
rep_idinteger or null
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/notes

Add a line to a lead's or a customer's note, through the app's own save (a lead's History logs "Note (API)").

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

lead_id or customer_id (not both). Adds a line; a note that would pass 500 (lead) / 4000 (customer) characters is 422 note_full, never cut.

FieldTypeNotes
lead_idinteger
customer_idstringcus_….
text requiredstringThe line to add. Up to 500 characters.

Answer 201

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
subject (next to data)object
idstringln_<id> a lead's History entry, cn_<id> a customer's note.
typestringOne of: note, voice_note.
textstring
rep_idinteger or null
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

Photos · 2 routes
GETv1/photos

Photo metadata, newest first (read-only). Pin photos need the knock map, job photos estimates and invoices; otherwise they are left out.

Parameters

NameInTypeNotes
sourcequerystringmedia, pin or job. One of: media, pin, job.
lead_idqueryinteger
job_idqueryintegerAn estimate / invoice id.
sincequerystringCreated after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringmed_<id> Photos hub, pin_<id> a pin's house photo, job_<id> an estimate / invoice photo.
sourcestringOne of: media, pin, job.
kindstringOne of: photo, video, audio.
mimestring or null
captionstring
tagstringA pin photo's tag, or before / after on a job photo.
lead_idinteger or null
job_idinteger or nullThe estimate / invoice id.
customer_namestring
addressstring
rep_idinteger or null
widthinteger or null
heightinteger or null
view_urlstring or nullThe app's own sign-in-gated route; there is no public photo link and the API serves no files.
captured_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/photos/{id}

One photo's metadata.

Parameters

NameInTypeNotes
id requiredpathstringmed_<id>, pin_<id> or job_<id>.

Answer 200

data is:

FieldTypeNotes
idstringmed_<id> Photos hub, pin_<id> a pin's house photo, job_<id> an estimate / invoice photo.
sourcestringOne of: media, pin, job.
kindstringOne of: photo, video, audio.
mimestring or null
captionstring
tagstringA pin photo's tag, or before / after on a job photo.
lead_idinteger or null
job_idinteger or nullThe estimate / invoice id.
customer_namestring
addressstring
rep_idinteger or null
widthinteger or null
heightinteger or null
view_urlstring or nullThe app's own sign-in-gated route; there is no public photo link and the API serves no files.
captured_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Reviews · 2 routes
GETv1/reviews

Star reviews on your digital cards, newest first (read-only).

Parameters

NameInTypeNotes
sincequerystringCreated after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
min_ratingqueryinteger1–5. 1 to 5.
hiddenquerystringOnly hidden (true) or shown (false) reviews. One of: true, false.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
sourcestringOne of: card.
ratinginteger1 to 5.
reviewer_namestring
textstring
hiddenbooleanHidden from the card in the app.
rep_idinteger
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/reviews/{id}

One review.

Parameters

NameInTypeNotes
id requiredpathintegerReview id.

Answer 200

data is:

FieldTypeNotes
idinteger
sourcestringOne of: card.
ratinginteger1 to 5.
reviewer_namestring
textstring
hiddenbooleanHidden from the card in the app.
rep_idinteger
created_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Knocks · 2 routes
GETv1/knocks

Door-knock history (worked doors), most recently changed first (read-only). Plan: the knock map.

Parameters

NameInTypeNotes
fromquerystringKnocked at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringKnocked before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
statusquerystringOne disposition key.
rep_idqueryintegerWho knocked it.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idintegerThe pin id (also works with GET v1/leads/{id}).
lead_idinteger
addressstring
latnumber or null
lngnumber or null
statusstringYour disposition key.
status_labelstringYour label for it.
rep_idintegerWho knocked it (else the assignee, else the pin's owner).
is_businessboolean
verifiedbooleanThe rep's phone was at the door.
first_knocked_atdate-time or nullISO 8601 with the business's UTC offset.
last_knocked_atdate-time or nullISO 8601 with the business's UTC offset.
won_atdate-time or nullISO 8601 with the business's UTC offset.
appointment_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/knocks/{id}

One worked door with its visits.

Parameters

NameInTypeNotes
id requiredpathintegerPin id.

Answer 200

data is:

FieldTypeNotes
idintegerThe pin id (also works with GET v1/leads/{id}).
lead_idinteger
addressstring
latnumber or null
lngnumber or null
statusstringYour disposition key.
status_labelstringYour label for it.
rep_idintegerWho knocked it (else the assignee, else the pin's owner).
is_businessboolean
verifiedbooleanThe rep's phone was at the door.
first_knocked_atdate-time or nullISO 8601 with the business's UTC offset.
last_knocked_atdate-time or nullISO 8601 with the business's UTC offset.
won_atdate-time or nullISO 8601 with the business's UTC offset.
appointment_atdate-time or nullISO 8601 with the business's UTC offset.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
visitsarray of objectThe pin's disposition changes, newest first.
visits[].changestringe.g. "Not home → Callback".
visits[].rep_idinteger or null
visits[].at_the_doorboolean or null
visits[].atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Campaigns · 3 routes
GETv1/campaigns

Marketing campaigns with counts, most recently changed first (read-only). Plan: the marketing suite.

Parameters

NameInTypeNotes
statusquerystringOne status. One of: active, paused, draft, archived.
updated_sincequerystringChanged at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idinteger
namestring
typestring or null
statusstringactive, paused, draft or archived.
servicestring
source_labelstring
goalstring or null
rep_idinteger
landing_urlstring or nullThe public page printed on its flyers and postcards.
statsobject
stats.sendsintegerPostcards mailed (test sends left out).
stats.opensintegerMailed households who scanned.
stats.scansintegerEvery scan.
stats.visitsintegerLanding-page visits.
stats.repliesintegerLeads it brought in.
stats.bookingsintegerThose leads with a booked visit.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
archived_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/campaigns/{id}

One campaign.

Parameters

NameInTypeNotes
id requiredpathintegerCampaign id.

Answer 200

data is:

FieldTypeNotes
idinteger
namestring
typestring or null
statusstringactive, paused, draft or archived.
servicestring
source_labelstring
goalstring or null
rep_idinteger
landing_urlstring or nullThe public page printed on its flyers and postcards.
statsobject
stats.sendsintegerPostcards mailed (test sends left out).
stats.opensintegerMailed households who scanned.
stats.scansintegerEvery scan.
stats.visitsintegerLanding-page visits.
stats.repliesintegerLeads it brought in.
stats.bookingsintegerThose leads with a booked visit.
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
archived_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/campaigns/summary

Your marketing in one answer: campaigns, direct mail, lit-up postcards.

Parameters

NameInTypeNotes
fromquerystringAt or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
toquerystringBefore (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.

Answer 200

data is:

FieldTypeNotes
fromdate-time or nullISO 8601 with the business's UTC offset.
todate-time or nullISO 8601 with the business's UTC offset.
campaignsobject
campaigns.countinteger
campaigns.visitsinteger
campaigns.repliesinteger
direct_mailobjectTest sends and canceled mailings left out.
direct_mail.mailingsinteger
direct_mail.sendsinteger
direct_mail.failedinteger
direct_mail.opensinteger
direct_mail.scansinteger
lit_up_postcardsobject
lit_up_postcards.sendsinteger
lit_up_postcards.opensinteger
lit_up_postcards.repliesinteger

Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.

Webhooks & tools

Your account (me) · 1 route
GETv1/me

The business this key belongs to (a connection test).

Answer 200

FieldTypeNotes
oktrue
company_idinteger
company_namestring
owner_namestring
timezonestringIANA zone, e.g. America/Los_Angeles.
api_versionstring
eventsarray of stringEvery webhook event key (GET v1/events has names and descriptions).

Errors: 403 (plus 401, 429, 500 on every route). What they mean.

Webhooks · 7 routes
GETv1/webhooks

Your webhooks (up to 5), oldest first. Never the signing secret.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
max_webhooks (next to data)integer
idinteger
labelstring
urlurihttps on a public host.
eventsarray of string
statusstringOne of: active, paused.
paused_reasonstring or nullowner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null.
paused_atdate-time or nullISO 8601 with the business's UTC offset.
failure_streakintegerFailed attempts in a row.
last_delivered_atdate-time or nullISO 8601 with the business's UTC offset.
last_failed_atdate-time or nullISO 8601 with the business's UTC offset.
secret_hintstring or nullThe last 4 of the signing secret ("whsec_…1a2b"). The secret itself is only in the 201 of POST v1/webhooks (and in the app).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/webhooks

Add a webhook. The answer carries the signing secret ONCE.

Same checks as Settings → API & webhooks: https on a public host (re-checked at every send, no redirects followed), at least one event, at most 5 webhooks. The same URL twice is 409 duplicate_url.

Parameters

NameInTypeNotes
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded)

FieldTypeNotes
url requiredurihttps on a public host (no private, loopback, link-local or reserved address; checked again at every send). Up to 600 characters.
events requiredarray of string or string
labelstringDefaults to the URL's host. Up to 80 characters.
pausedbooleanDefault false.

Answer 201

data is:

FieldTypeNotes
idinteger
labelstring
urlurihttps on a public host.
eventsarray of string
statusstringOne of: active, paused.
paused_reasonstring or nullowner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null.
paused_atdate-time or nullISO 8601 with the business's UTC offset.
failure_streakintegerFailed attempts in a row.
last_delivered_atdate-time or nullISO 8601 with the business's UTC offset.
last_failed_atdate-time or nullISO 8601 with the business's UTC offset.
secret_hintstring or nullThe last 4 of the signing secret ("whsec_…1a2b"). The secret itself is only in the 201 of POST v1/webhooks (and in the app).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
secretstring or nullThe signing secret (whsec_…), shown ONCE. null when this answer is an Idempotency-Key replay.

Errors: 400 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/webhooks/{id}

One webhook.

Parameters

NameInTypeNotes
id requiredpathintegerWebhook id.

Answer 200

data is:

FieldTypeNotes
idinteger
labelstring
urlurihttps on a public host.
eventsarray of string
statusstringOne of: active, paused.
paused_reasonstring or nullowner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null.
paused_atdate-time or nullISO 8601 with the business's UTC offset.
failure_streakintegerFailed attempts in a row.
last_delivered_atdate-time or nullISO 8601 with the business's UTC offset.
last_failed_atdate-time or nullISO 8601 with the business's UTC offset.
secret_hintstring or nullThe last 4 of the signing secret ("whsec_…1a2b"). The secret itself is only in the 201 of POST v1/webhooks (and in the app).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

PATCHv1/webhooks/{id}

Change its label, URL or events, or pause / resume it.

Parameters

NameInTypeNotes
id requiredpathintegerWebhook id.

Body (JSON or form-encoded)

Send at least one field. Only what is sent changes.

FieldTypeNotes
urluriUp to 600 characters.
eventsarray of string or string
labelstringUp to 80 characters.
pausedbooleantrue pauses (queued deliveries are held); false resumes (held deliveries younger than 3 days are sent).

Answer 200

data is:

FieldTypeNotes
idinteger
labelstring
urlurihttps on a public host.
eventsarray of string
statusstringOne of: active, paused.
paused_reasonstring or nullowner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null.
paused_atdate-time or nullISO 8601 with the business's UTC offset.
failure_streakintegerFailed attempts in a row.
last_delivered_atdate-time or nullISO 8601 with the business's UTC offset.
last_failed_atdate-time or nullISO 8601 with the business's UTC offset.
secret_hintstring or nullThe last 4 of the signing secret ("whsec_…1a2b"). The secret itself is only in the 201 of POST v1/webhooks (and in the app).
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

DELETEv1/webhooks/{id}

Delete it. Its queued deliveries are dropped; the log keeps the finished ones.

Parameters

NameInTypeNotes
id requiredpathintegerWebhook id.

Answer 200

data is:

FieldTypeNotes
idinteger
deletedtrue

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

POSTv1/webhooks/{id}/test

Send a signed test event ("test": true) to it now. Works while paused; never retried. At most 5 a minute.

Parameters

NameInTypeNotes
id requiredpathintegerWebhook id.
Idempotency-Keyheaderstring1–100 visible ASCII characters. A retry with the same key and body within 24 h gets the first answer (Idempotent-Replayed: true). Up to 100 characters.

Body (JSON or form-encoded), optional

FieldTypeNotes
eventstringDefault: the webhook's first event. One of: lead.created, lead.status_changed, lead.sold, appointment.created, quote.approved, invoice.paid, booking.requested, booking.rescheduled, booking.canceled, website.form, campaign.lead, chat.lead, card.lead, review.received, lighting.reply, lighting.postcard_opened.

Answer 200

data is:

FieldTypeNotes
webhook_idinteger
eventstring
deliveredbooleanThe receiver answered 2xx.
status_codeinteger or null
errorstring or null
response_snippetstringThe receiver's answer, cleaned, ≤300 characters.

Errors: 400 · 404 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/webhooks/deliveries

The delivery log (30 days), newest first.

Parameters

NameInTypeNotes
webhook_idqueryintegerOnly this webhook.
eventquerystringOnly this event. One of: lead.created, lead.status_changed, lead.sold, appointment.created, quote.approved, invoice.paid, booking.requested, booking.rescheduled, booking.canceled, website.form, campaign.lead, chat.lead, card.lead, review.received, lighting.reply, lighting.postcard_opened.
statusquerystringComma-separated: delivered, failed, retrying, queued, sending, held, skipped.
sincequerystringQueued at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds.
limitqueryinteger1–100. 1 to 100. Default 50.
cursorquerystringnext_cursor from the previous page (same route and filters). Up to 400 characters.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
idstringdlv_…: the X-FieldStacker-Delivery header (unchanged across retries).
eventstring
event_idstring or nullevt_…: the body's id.
testboolean
webhook_idinteger or nullnull for a note row that was never sent.
webhook_labelstringThe label when it was queued.
statusstringOne of: delivered, failed, retrying, queued, sending, held, skipped.
attemptsinteger
max_attemptsinteger
will_retryboolean
next_attempt_atdate-time or nullISO 8601 with the business's UTC offset.
last_status_codeinteger or null
last_errorstring or null
response_snippetstring
duration_msinteger or null
created_atdate-time or nullISO 8601 with the business's UTC offset.
updated_atdate-time or nullISO 8601 with the business's UTC offset.
completed_atdate-time or nullISO 8601 with the business's UTC offset.

Errors: 400 · 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Events · 2 routes
GETv1/events

Every event a webhook can subscribe to.

Answer 200

A list: data is an array of these (with next_cursor and limit).

FieldTypeNotes
api_version (next to data)string
keystringOne of: lead.created, lead.status_changed, lead.sold, appointment.created, quote.approved, invoice.paid, booking.requested, booking.rescheduled, booking.canceled, website.form, campaign.lead, chat.lead, card.lead, review.received, lighting.reply, lighting.postcard_opened.
namestring
descriptionstring

Errors: 403 (plus 401, 429, 500 on every route). What they mean.

GETv1/events/{event}

One event type + a sample payload (nothing is sent).

Parameters

NameInTypeNotes
event requiredpathstringAn event key, e.g. lead.created. One of: lead.created, lead.status_changed, lead.sold, appointment.created, quote.approved, invoice.paid, booking.requested, booking.rescheduled, booking.canceled, website.form, campaign.lead, chat.lead, card.lead, review.received, lighting.reply, lighting.postcard_opened.

Answer 200

data is:

FieldTypeNotes
keystringOne of: lead.created, lead.status_changed, lead.sold, appointment.created, quote.approved, invoice.paid, booking.requested, booking.rescheduled, booking.canceled, website.form, campaign.lead, chat.lead, card.lead, review.received, lighting.reply, lighting.postcard_opened.
namestring
descriptionstring
headersarray of string
sampleobjectA sample body ("test": true): the envelope (id, event, test, api_version, occurred_at, occurred_at_local, timezone, company_id, company_name) + the event's flat fields.

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

Usage · 1 route
GETv1/usage

This key's rate-limit window and request counts (read-only).

Answer 200

data is:

FieldTypeNotes
keyobject
key.prefixstringThe first 10 characters ("fsk_1a2b3c").
key.created_atdate-time or nullISO 8601 with the business's UTC offset.
key.last_used_atdate-time or nullISO 8601 with the business's UTC offset.
rate_limitobject
rate_limit.limitintegerRequests per key per window.
rate_limit.window_secondsinteger
rate_limit.usedintegerIn the current window, this call included.
rate_limit.remaininginteger
rate_limit.resets_atdate-time or nullWhen the oldest call in the window ages out. ISO 8601 with the business's UTC offset.
requestsobjectEvery count includes this call.
requests.last_hourinteger
requests.todayintegerSince local midnight.
requests.last_24_hoursinteger
requests.last_30_daysinteger
requests.errors_last_24_hoursintegerStatus 400 and up.
requests.rate_limited_last_24_hoursintegerStatus 429.
by_dayarray of objectNewest first, local dates, days with calls only (this call not included).
by_day[].datedate
by_day[].requestsinteger
by_day[].errorsinteger
by_day[].rate_limitedinteger
retention_daysinteger
timezonestring

Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.

OpenAPI description · 1 route
GETv1/openapi.json no key needed

This description. No key needed.

Answer 200

An OpenAPI 3.1 document.

Questions or a bug? Email support@fieldstacker.com.