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
Customers, most recent activity first. Plan: Customers.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| q | query | string | Search name, business, phone (or its digits), email, address, tags across every record. Up to 100 characters. |
| updated_since | query | string | Last activity or edit at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string or null | cus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id. |
| name | string | |
| business | string | |
| phone | string | |
| alt_phone | string | |
| string | ||
| address | string | |
| tags | array of string | |
| notes | string | |
| record_ids | array of string | Every one resolves to this customer. |
| records | object | |
| records.quotes | integer | |
| records.card_contacts | integer | |
| records.estimates_and_invoices | integer | |
| records.appointments | integer | |
| records.doors | integer | Door trades only. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | Last 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.
Add a customer (like + Add customer). An existing match answers 200 existing: true and writes nothing.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| name | string | Up to 120 characters. |
| phone | string | Up to 40 characters. |
| Up to 160 characters. | ||
| address | string | Up to 300 characters. |
| notes | string | Up to 500 characters. |
| lat | number | -90 to 90. |
| lng | number | -180 to 180. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| existing (next to data) | true | |
| id | string or null | cus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id. |
| name | string | |
| business | string | |
| phone | string | |
| alt_phone | string | |
| string | ||
| address | string | |
| tags | array of string | |
| notes | string | |
| record_ids | array of string | Every one resolves to this customer. |
| records | object | |
| records.quotes | integer | |
| records.card_contacts | integer | |
| records.estimates_and_invoices | integer | |
| records.appointments | integer | |
| records.doors | integer | Door trades only. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | Last 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.
One customer.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | cus_… — any of the customer's record ids. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string or null | cus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id. |
| name | string | |
| business | string | |
| phone | string | |
| alt_phone | string | |
| string | ||
| address | string | |
| tags | array of string | |
| notes | string | |
| record_ids | array of string | Every one resolves to this customer. |
| records | object | |
| records.quotes | integer | |
| records.card_contacts | integer | |
| records.estimates_and_invoices | integer | |
| records.appointments | integer | |
| records.doors | integer | Door trades only. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | Last activity or edit. ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Edit what FieldStacker shows for the customer (like ✏️ Edit customer).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | cus_… — 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).
| Field | Type | Notes |
|---|---|---|
| name | string | Up to 120 characters. |
| business | string | Up to 120 characters. |
| phone | string | Up to 40 characters. |
| alt_phone | string | Up to 40 characters. |
| string | Up to 160 characters. | |
| address | string | Up to 300 characters. |
| notes | string | Up to 4000 characters. |
| tags | string or array of string |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string or null | cus_ + kind letter + record id (q quote, c card contact, s estimate/invoice, a appointment, k won door). The OLDEST record's id. |
| name | string | |
| business | string | |
| phone | string | |
| alt_phone | string | |
| string | ||
| address | string | |
| tags | array of string | |
| notes | string | |
| record_ids | array of string | Every one resolves to this customer. |
| records | object | |
| records.quotes | integer | |
| records.card_contacts | integer | |
| records.estimates_and_invoices | integer | |
| records.appointments | integer | |
| records.doors | integer | Door trades only. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | Last 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
Leads created since a time (a bare JSON array, newest first — the polling shape).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| since | query | string | Default 7 days ago. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100 (default 100). 1 to 100. |
Answer 200
A JSON array of:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| rep_id | integer or null | The rep the pin belongs to (assigned rep, else its owner). |
| rep_name | string | |
| rep_email | string | |
| lead_id | integer | Same as id. |
| lead_name | string | |
| lead_business_name | string | Set when the pin is a business. |
| lead_contact_name | string | |
| lead_contact_position | string | |
| lead_phone | string | |
| lead_email | string | |
| lead_address | string | |
| lead_lat | number or null | |
| lead_lng | number or null | |
| lead_status | string | The disposition key. |
| lead_status_label | string | The business's own label for it. |
| lead_notes | string | Cut to 300 characters. |
| lead_source | string | map, import, api, appointment, … |
| lead_category | string | |
| lead_is_business | boolean | |
| lead_created_at | string | ISO 8601 UTC ("…Z"). |
| lead_url | string | Opens the pin in FieldStacker. |
Errors: 422 · 403 (plus 401, 429, 500 on every route). What they mean.
Add a lead (a pin on the map). Fires lead.created unless notify_webhooks is false.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| name | string | Up to 120 characters. |
| business_name | string | Makes the pin a business. Up to 120 characters. |
| contact_name | string | Up to 120 characters. |
| position | string | Up to 80 characters. |
| phone | string | Up to 40 characters. |
| Up to 255 characters. | ||
| address | string | Geocoded when lat/lng are not sent (cache first, then one lookup under the owner's daily cap). Up to 255 characters. |
| lat | number | -90 to 90. |
| lng | number | -180 to 180. |
| notes | string | Up to 500 characters. |
| category | string | Up to 60 characters. |
| status | string | A disposition key or label (default lead). |
| assign_to | A teammate's email. | |
| external_id | string | Your id: the same one again returns the first lead with duplicate: true. Up to 100 characters. |
| notify_webhooks | boolean | false = do not fire lead.created (avoids loops). Default true. |
Answer 200
| Field | Type | Notes |
|---|---|---|
| ok | true | |
| duplicate | boolean | Present (true) when external_id / Idempotency-Key matched an existing lead. |
| lead | object | A lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape). |
| lead.id | integer | |
| lead.rep_id | integer or null | The rep the pin belongs to (assigned rep, else its owner). |
| lead.rep_name | string | |
| lead.rep_email | string | |
| lead.lead_id | integer | Same as id. |
| lead.lead_name | string | |
| lead.lead_business_name | string | Set when the pin is a business. |
| lead.lead_contact_name | string | |
| lead.lead_contact_position | string | |
| lead.lead_phone | string | |
| lead.lead_email | string | |
| lead.lead_address | string | |
| lead.lead_lat | number or null | |
| lead.lead_lng | number or null | |
| lead.lead_status | string | The disposition key. |
| lead.lead_status_label | string | The business's own label for it. |
| lead.lead_notes | string | Cut to 300 characters. |
| lead.lead_source | string | map, import, api, appointment, … |
| lead.lead_category | string | |
| lead.lead_is_business | boolean | |
| lead.lead_created_at | string | ISO 8601 UTC ("…Z"). |
| lead.lead_url | string | Opens the pin in FieldStacker. |
Errors: 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
One lead.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Lead id. |
Answer 200
| Field | Type | Notes |
|---|---|---|
| ok | true | |
| lead | object | A lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape). |
| lead.id | integer | |
| lead.rep_id | integer or null | The rep the pin belongs to (assigned rep, else its owner). |
| lead.rep_name | string | |
| lead.rep_email | string | |
| lead.lead_id | integer | Same as id. |
| lead.lead_name | string | |
| lead.lead_business_name | string | Set when the pin is a business. |
| lead.lead_contact_name | string | |
| lead.lead_contact_position | string | |
| lead.lead_phone | string | |
| lead.lead_email | string | |
| lead.lead_address | string | |
| lead.lead_lat | number or null | |
| lead.lead_lng | number or null | |
| lead.lead_status | string | The disposition key. |
| lead.lead_status_label | string | The business's own label for it. |
| lead.lead_notes | string | Cut to 300 characters. |
| lead.lead_source | string | map, import, api, appointment, … |
| lead.lead_category | string | |
| lead.lead_is_business | boolean | |
| lead.lead_created_at | string | ISO 8601 UTC ("…Z"). |
| lead.lead_url | string | Opens the pin in FieldStacker. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Change status and/or notes. Fires lead.status_changed (+ lead.sold / appointment.created).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Lead id. |
Body (JSON or form-encoded)
| Field | Type | Notes |
|---|---|---|
| status | string | A disposition key or label. |
| notes | string | Replaces the notes. Up to 500 characters. |
| append_note | string | Adds a line. |
Answer 200
| Field | Type | Notes |
|---|---|---|
| ok | true | |
| lead | object | A lead (map pin): the flat fields every lead webhook carries. Numbers are numbers here (the older shape). |
| lead.id | integer | |
| lead.rep_id | integer or null | The rep the pin belongs to (assigned rep, else its owner). |
| lead.rep_name | string | |
| lead.rep_email | string | |
| lead.lead_id | integer | Same as id. |
| lead.lead_name | string | |
| lead.lead_business_name | string | Set when the pin is a business. |
| lead.lead_contact_name | string | |
| lead.lead_contact_position | string | |
| lead.lead_phone | string | |
| lead.lead_email | string | |
| lead.lead_address | string | |
| lead.lead_lat | number or null | |
| lead.lead_lng | number or null | |
| lead.lead_status | string | The disposition key. |
| lead.lead_status_label | string | The business's own label for it. |
| lead.lead_notes | string | Cut to 300 characters. |
| lead.lead_source | string | map, import, api, appointment, … |
| lead.lead_category | string | |
| lead.lead_is_business | boolean | |
| lead.lead_created_at | string | ISO 8601 UTC ("…Z"). |
| lead.lead_url | string | Opens the pin in FieldStacker. |
Errors: 404 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
Estimates · 5 routes
Estimates, most recently changed first (read-only). Plan: business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | draft, sent or approved. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, sent, approved. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| customer_id | string | An existing customer (cus_…): its name, phone, email and address are filled in. |
| customer | object | Fields sent here win over the customer_id ones. |
| customer.name | string | Up to 160 characters. |
| customer.phone | string | Up to 40 characters. |
| customer.email | Up to 160 characters. | |
| customer.address | string | Up to 300 characters. |
| customer.contact_name | string | Up to 120 characters. |
| lines | array of object | Discounts are added after the other lines. Your automatic line rules add their lines too, as in the app. |
| lines[].type | string | One of: item, discount. Default "item". |
| lines[].product_id | integer | A 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[].name | string | Required on a line of your own. Default "Discount" on a discount. Up to 160 characters. |
| lines[].quantity | string or number | More than 0, default 1. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].unit_price | string or number | Required on a line of your own. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].amount | string or number | Discount 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:
| Field | Type | Notes |
|---|---|---|
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, sent, approved. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
One estimate.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Estimate id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, sent, approved. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Replace a DRAFT estimate's customer lines (a sent, approved, in-progress or paid one is 409 not_draft).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Estimate 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.
| Field | Type | Notes |
|---|---|---|
| lines required | array of object | The WHOLE new list of the customer's lines (hidden lines are never touched). |
| lines[].type | string | One of: item, discount. Default "item". |
| lines[].product_id | integer | A 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[].name | string | Required on a line of your own. Default "Discount" on a discount. Up to 160 characters. |
| lines[].quantity | string or number | More than 0, default 1. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].unit_price | string or number | Required on a line of your own. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].amount | string or number | Discount 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:
| Field | Type | Notes |
|---|---|---|
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, sent, approved. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
⚠️ 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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Estimate id. |
| Idempotency-Key | header | string | 1–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:
| Field | Type | Notes |
|---|---|---|
| sent (next to data) | object | |
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, sent, approved. |
| approved_at | date-time or null | ISO 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
Invoices, most recently changed first (read-only). Plan: business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | draft, in_progress, sent, unpaid or paid. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, in_progress, sent, unpaid, paid. |
| invoice_date | date or null | |
| due_date | date or null | |
| amount_paid | money string | |
| deposit_paid | money string | |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| payment_method | string or null | e.g. Cash, Card. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| customer_id | string | An existing customer (cus_…): its name, phone, email and address are filled in. |
| customer | object | Fields sent here win over the customer_id ones. |
| customer.name | string | Up to 160 characters. |
| customer.phone | string | Up to 40 characters. |
| customer.email | Up to 160 characters. | |
| customer.address | string | Up to 300 characters. |
| customer.contact_name | string | Up to 120 characters. |
| lines | array of object | Discounts are added after the other lines. Your automatic line rules add their lines too, as in the app. |
| lines[].type | string | One of: item, discount. Default "item". |
| lines[].product_id | integer | A 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[].name | string | Required on a line of your own. Default "Discount" on a discount. Up to 160 characters. |
| lines[].quantity | string or number | More than 0, default 1. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].unit_price | string or number | Required on a line of your own. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].amount | string or number | Discount 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:
| Field | Type | Notes |
|---|---|---|
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, in_progress, sent, unpaid, paid. |
| invoice_date | date or null | |
| due_date | date or null | |
| amount_paid | money string | |
| deposit_paid | money string | |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| payment_method | string or null | e.g. Cash, Card. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
One invoice.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Invoice id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, in_progress, sent, unpaid, paid. |
| invoice_date | date or null | |
| due_date | date or null | |
| amount_paid | money string | |
| deposit_paid | money string | |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| payment_method | string or null | e.g. Cash, Card. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Replace a DRAFT invoice's customer lines (a sent, approved, in-progress or paid one is 409 not_draft).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Invoice 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.
| Field | Type | Notes |
|---|---|---|
| lines required | array of object | The WHOLE new list of the customer's lines (hidden lines are never touched). |
| lines[].type | string | One of: item, discount. Default "item". |
| lines[].product_id | integer | A 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[].name | string | Required on a line of your own. Default "Discount" on a discount. Up to 160 characters. |
| lines[].quantity | string or number | More than 0, default 1. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].unit_price | string or number | Required on a line of your own. A JSON number or a decimal string, at most 2 decimal places. |
| lines[].amount | string or number | Discount 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:
| Field | Type | Notes |
|---|---|---|
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, in_progress, sent, unpaid, paid. |
| invoice_date | date or null | |
| due_date | date or null | |
| amount_paid | money string | |
| deposit_paid | money string | |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| payment_method | string or null | e.g. Cash, Card. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
⚠️ 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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Invoice id. |
| Idempotency-Key | header | string | 1–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:
| Field | Type | Notes |
|---|---|---|
| sent (next to data) | object | |
| lines | array of object | |
| lines[].id | integer or null | The line's id. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| id | integer | |
| number | string | |
| type | string | One of: estimate, invoice. |
| customer | object | |
| customer.id | string or null | cus_s<id>: the customer this document belongs to (GET v1/customers/{id}). |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| subtotal | money string | |
| labor | money string | |
| tax_rate | string | Percent, e.g. "8.25". |
| tax | money string | |
| total | money string | |
| notes | string | |
| customer_url | string or null | The document's existing share link (never created by the API). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| status | string | One of: draft, in_progress, sent, unpaid, paid. |
| invoice_date | date or null | |
| due_date | date or null | |
| amount_paid | money string | |
| deposit_paid | money string | |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| payment_method | string or null | e.g. Cash, Card. |
| completed_at | date-time or null | ISO 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
Money received, newest first (read-only). Plan: business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| since | query | string | Paid at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| until | query | string | Paid before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| type | query | string | payment or deposit. One of: payment, deposit. |
| method | query | string | One method. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other. |
| invoice_id | query | integer | Only this invoice's payments and deposits. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | pay_s<invoice> · dep_s<invoice> · dep_c<change order> · pay_q<quote> (card) · pay_k<quote> (recorded by hand) · pay_v<recurring visit>. |
| type | string | One of: payment, deposit. |
| amount | money string | An invoice payment is the bill less every deposit listed, so its payments add up to amount_paid. |
| currency | string | |
| method | string or null | null on an old row with none recorded. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other, null. |
| method_label | string or null | |
| card_brand | string or null | Only when FieldStacker stored it. |
| card_last4 | string or null | Only when FieldStacker stored it. |
| status | string | One of: succeeded, refunded, disputed. |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| invoice_id | integer or null | |
| estimate_id | integer or null | |
| change_order | integer or null | The change order's number (its deposit). |
| quote_id | integer or null | |
| customer_id | string or null | |
| source | string | One of: invoice, invoice_deposit, change_order_deposit, quote, recurring_plan. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One payment.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | pay_s…, dep_s…, dep_c…, pay_q…, pay_k… or pay_v…. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | pay_s<invoice> · dep_s<invoice> · dep_c<change order> · pay_q<quote> (card) · pay_k<quote> (recorded by hand) · pay_v<recurring visit>. |
| type | string | One of: payment, deposit. |
| amount | money string | An invoice payment is the bill less every deposit listed, so its payments add up to amount_paid. |
| currency | string | |
| method | string or null | null on an old row with none recorded. One of: cash, check, card, card_other, zelle, venmo, cashapp, paypal, split, other, null. |
| method_label | string or null | |
| card_brand | string or null | Only when FieldStacker stored it. |
| card_last4 | string or null | Only when FieldStacker stored it. |
| status | string | One of: succeeded, refunded, disputed. |
| paid_at | date-time or null | ISO 8601 with the business's UTC offset. |
| invoice_id | integer or null | |
| estimate_id | integer or null | |
| change_order | integer or null | The change order's number (its deposit). |
| quote_id | integer or null | |
| customer_id | string or null | |
| source | string | One 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
Quote-builder quotes, newest first (read-only). Plan: quoting.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | One status. One of: quoted, viewed, expired, approved, install_pending, installed, declined. |
| since | query | string | Created at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| until | query | string | Created before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| type | string | One of: install, fiber, cleaning, service. |
| status | string | One of: quoted, viewed, expired, approved, install_pending, installed, declined. |
| customer | object | |
| customer.id | string or null | cus_q<id>, null when the quote has no contact. |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| plan | object | |
| plan.name | string | |
| plan.term_months | integer or null | |
| plan.monthly | money string or null | |
| plan.upfront | money string or null | |
| plan.total_value | money string or null | |
| price | object or null | The quote's money in its own terms; null when nothing was priced. |
| price.amount | money string | |
| price.per | string or null | One of: month, visit, null. |
| price.cadence | string or null | |
| frequency | string | one_time, or a recurring clean's frequency. |
| currency | string | |
| lines | array of object | The customer-facing breakdown the quote showed. |
| lines[].name | string | |
| lines[].detail | string | |
| lines[].price | money string | |
| lines[].included | boolean | |
| lines[].monthly | money string | Present on a monthly line. |
| equipment | array of object | |
| equipment[].key | string | |
| equipment[].name | string | |
| equipment[].quantity | integer | 1 to 999. |
| invoice_id | integer or null | The invoice the quote is billed through. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| expires_at | date-time or null | ISO 8601 with the business's UTC offset. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
| installed_on | date or null | |
| install_verified | boolean | |
| declined_on | date or null |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One quote.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Quote id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| type | string | One of: install, fiber, cleaning, service. |
| status | string | One of: quoted, viewed, expired, approved, install_pending, installed, declined. |
| customer | object | |
| customer.id | string or null | cus_q<id>, null when the quote has no contact. |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| plan | object | |
| plan.name | string | |
| plan.term_months | integer or null | |
| plan.monthly | money string or null | |
| plan.upfront | money string or null | |
| plan.total_value | money string or null | |
| price | object or null | The quote's money in its own terms; null when nothing was priced. |
| price.amount | money string | |
| price.per | string or null | One of: month, visit, null. |
| price.cadence | string or null | |
| frequency | string | one_time, or a recurring clean's frequency. |
| currency | string | |
| lines | array of object | The customer-facing breakdown the quote showed. |
| lines[].name | string | |
| lines[].detail | string | |
| lines[].price | money string | |
| lines[].included | boolean | |
| lines[].monthly | money string | Present on a monthly line. |
| equipment | array of object | |
| equipment[].key | string | |
| equipment[].name | string | |
| equipment[].quantity | integer | 1 to 999. |
| invoice_id | integer or null | The invoice the quote is billed through. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| expires_at | date-time or null | ISO 8601 with the business's UTC offset. |
| approved_at | date-time or null | ISO 8601 with the business's UTC offset. |
| installed_on | date or null | |
| install_verified | boolean | |
| declined_on | date or null |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Products · 2 routes
The price list, by id (read-only). Plan: quoting or business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| q | query | string | Name or SKU contains. Up to 100 characters. |
| category | query | string | Exact category (any case). Up to 40 characters. |
| active | query | string | true (default), false (hidden items) or all. One of: true, false, all. Default "true". |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| key | string | The price-list key. |
| name | string | |
| sku | string | The model / part number. |
| category | string | |
| price | money string | The customer price. |
| currency | string | |
| unit | string | Always each: every line is quantity × price. One of: each. |
| taxable | true | Always true: an estimate or invoice charges its tax rate on every line. |
| active | boolean | false = a hidden item. |
| shown_on_website | boolean |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One product (a hidden one answers with active: false).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Product id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| key | string | The price-list key. |
| name | string | |
| sku | string | The model / part number. |
| category | string | |
| price | money string | The customer price. |
| currency | string | |
| unit | string | Always each: every line is quantity × price. One of: each. |
| taxable | true | Always true: an estimate or invoice charges its tax rate on every line. |
| active | boolean | false = a hidden item. |
| shown_on_website | boolean |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Services (price list) · 1 route
The price list (not hidden items). Plan: quoting or business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| q | query | string | Name or model contains. Up to 100 characters. |
| category | query | string | Exact category (any case). Up to 40 characters. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| key | string | |
| name | string | |
| model | string | |
| category | string | |
| price | money string | |
| currency | string | |
| shown_on_website | boolean |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
Form leads · 2 routes
Website, landing-page and card form leads, newest first (read-only).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| since | query | string | Created after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| source | query | string | website_message, website_form, landing_page or card. One of: website_message, website_form, landing_page, card. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | msg_<id> | lp_<id> | card_<id> |
| source | string | One of: website_message, website_form, landing_page, card. |
| name | string | |
| string | ||
| phone | string | |
| address | string | |
| message | string | |
| campaign_id | integer or null | |
| customer_id | string or null | The customer the lead became (null for website messages). |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One form lead.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | msg_<id>, lp_<id> or card_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | msg_<id> | lp_<id> | card_<id> |
| source | string | One of: website_message, website_form, landing_page, card. |
| name | string | |
| string | ||
| phone | string | |
| address | string | |
| message | string | |
| campaign_id | integer or null | |
| customer_id | string or null | The customer the lead became (null for website messages). |
| created_at | date-time or null | ISO 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
Online bookings and schedule appointments, by start time (oldest first).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| from | query | string | Start at or after (default 30 days ago). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Start before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| status | query | string | Comma-separated: pending, confirmed, canceled, declined, completed, no_show. |
| type | query | string | online_booking or appointment. One of: online_booking, appointment. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | bk_<id> online booking, ap_<id> schedule appointment. |
| type | string | One of: online_booking, appointment. |
| status | string | One of: pending, confirmed, canceled, declined, completed, no_show. |
| title | string | |
| service | string or null | The booking page service key (online bookings only). |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| notes | string | |
| job_number | string or null | Schedule appointments only. |
| changeable | boolean | true = PATCH may move / cancel it. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| confirmed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| start_at required | date-time | One of the open times from GET v1/availability (a date alone is refused). |
| service | string | A service key from GET v1/availability. Up to 60 characters. |
| customer required | object | |
| customer.name required | string | Up to 120 characters. |
| customer.email | Up to 160 characters. | |
| customer.phone | string | Up to 40 characters. |
| customer.address | string | Up to 300 characters. |
| customer.notes | string | Up to 1000 characters. |
| customer.answers | object | Intake question key → answer (GET v1/availability lists the questions). |
Answer 201
data is:
| Field | Type | Notes |
|---|---|---|
| needs_approval (next to data) | boolean | |
| id | string | bk_<id> online booking, ap_<id> schedule appointment. |
| type | string | One of: online_booking, appointment. |
| status | string | One of: pending, confirmed, canceled, declined, completed, no_show. |
| title | string | |
| service | string or null | The booking page service key (online bookings only). |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| notes | string | |
| job_number | string or null | Schedule appointments only. |
| changeable | boolean | true = PATCH may move / cancel it. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| confirmed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
One booking.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | bk_<id> or ap_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | bk_<id> online booking, ap_<id> schedule appointment. |
| type | string | One of: online_booking, appointment. |
| status | string | One of: pending, confirmed, canceled, declined, completed, no_show. |
| title | string | |
| service | string or null | The booking page service key (online bookings only). |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| notes | string | |
| job_number | string or null | Schedule appointments only. |
| changeable | boolean | true = PATCH may move / cancel it. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| confirmed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Move, cancel or confirm an online booking (website service bookings: move or cancel).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | bk_<id> or ap_<id>. |
Body (JSON or form-encoded)
Send start_at or status (not start_at with canceled).
| Field | Type | Notes |
|---|---|---|
| start_at | date-time | Move it (an open time). |
| status | string | One of: canceled, confirmed. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | bk_<id> online booking, ap_<id> schedule appointment. |
| type | string | One of: online_booking, appointment. |
| status | string | One of: pending, confirmed, canceled, declined, completed, no_show. |
| title | string | |
| service | string or null | The booking page service key (online bookings only). |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| notes | string | |
| job_number | string or null | Schedule appointments only. |
| changeable | boolean | true = PATCH may move / cancel it. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| confirmed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 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
Open booking times, the services and the intake questions.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| date | query | date | YYYY-MM-DD: the open start times that day. Without it: the next dates with any. |
| service | query | string | A service key. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| enabled | boolean | Online booking is on. |
| needs_approval | boolean | |
| timezone | string | |
| duration_min | integer | |
| services | array of object | |
| services[].key | string | |
| services[].label | string | |
| questions | array of object | |
| questions[].key | string | |
| questions[].label | string | |
| questions[].type | string | |
| questions[].required | boolean | |
| questions[].options | array of string | |
| dates | array of date | Without ?date: the next days with an open time (≤30). |
| date | date | With ?date. |
| slots | array of object | With ?date: the open start times. |
| slots[].start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| slots[].end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| slots[].label | string |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
Schedule · 1 route
One calendar feed (jobs, appointments, online bookings, recurring-clean visits awaiting a date), by start time. Plan: the Schedule board or business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| from | query | string | Default: today. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Default: 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_id | query | integer | A person id from GET v1/team. |
| kind | query | string | Comma-separated: job, appointment, online_booking, plan_visit. |
| include_canceled | query | string | true adds canceled and declined entries. One of: true, false. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>, bk_<id> or cpv_<id>: the record's own id. |
| kind | string | One of: job, appointment, online_booking, plan_visit. |
| job_type | string or null | One of: install, service_booking, service_call, plan_visit, job, null. |
| status | string | A job's board status, a booking's status, or awaiting_confirmation for a plan visit. |
| title | string | |
| start_at | date-time or null | null on an all-day plan visit. ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| date | string | YYYY-MM-DD. |
| all_day | boolean | |
| timezone | string | |
| person | object or null | |
| person.id | integer | |
| person.name | string | |
| customer | object | |
| customer.name | string | |
| customer.address | string | |
| resource | string | The 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
Jobs on the schedule, most recently changed first. Plan: business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | Comma-separated: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| type | query | string | Comma-separated: install, service_booking, service_call, plan_visit, job. |
| from | query | string | Start at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Start before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| tech_id | query | integer | A person id from GET v1/team. |
| customer_id | query | string | A cus_… id from GET v1/customers: every job on that customer's records. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>: the same id GET v1/bookings uses. |
| type | string | One of: install, service_booking, service_call, plan_visit, job. |
| status | string | The Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| title | string | |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| arrival_window_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| assigned_to | object or null | Who does it. |
| assigned_to.id | integer | |
| assigned_to.name | string | |
| offer_status | string or null | The tech's answer to the offer. One of: pending, accepted, declined, null. |
| booked_by | object | |
| booked_by.id | integer | |
| booked_by.name | string | |
| job_number | string or null | |
| notes | string | |
| invoice_id | integer or null | GET v1/invoices/{id}. |
| estimate_id | integer or null | GET v1/estimates/{id}. |
| quote_id | integer or null | |
| plan_id | string or null | sp_<id> or cp_<id> on a plan visit. |
| currency | string | |
| lines | array of object | The linked invoice's (or estimate's) customer lines. Never a hidden line. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| equipment | array of object | What goes in: names and quantities only. |
| equipment[].name | string | |
| equipment[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| en_route_at | date-time or null | ISO 8601 with the business's UTC offset. |
| checked_in_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | The 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.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| tech_id required | integer | A person with assignable: true. |
| start_at required | date-time | Date AND time, in the future (read in the tech's own zone). |
| arrival_window | boolean | true = a 2-hour arrival window from start_at. Default false. |
| customer | object | May also be sent flat. |
| customer.name required | string | Up to 200 characters. |
| customer.phone | string | Up to 40 characters. |
| customer.email | Up to 160 characters. | |
| customer.address | string | Up to 500 characters. |
| customer.lat | number | -90 to 90. |
| customer.lng | number | -180 to 180. |
| items required | array of object | |
| items[].name required | string | Up to 160 characters. |
| items[].quantity | integer | Default 1. 1 to 999. |
| items[].key | string | Your catalog key. Up to 60 characters. |
| job_number | string | Up to 60 characters. |
| force | boolean | true = dispatch even if the tech is short of tracked equipment. Default false. |
Answer 201
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>: the same id GET v1/bookings uses. |
| type | string | One of: install, service_booking, service_call, plan_visit, job. |
| status | string | The Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| title | string | |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| arrival_window_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| assigned_to | object or null | Who does it. |
| assigned_to.id | integer | |
| assigned_to.name | string | |
| offer_status | string or null | The tech's answer to the offer. One of: pending, accepted, declined, null. |
| booked_by | object | |
| booked_by.id | integer | |
| booked_by.name | string | |
| job_number | string or null | |
| notes | string | |
| invoice_id | integer or null | GET v1/invoices/{id}. |
| estimate_id | integer or null | GET v1/estimates/{id}. |
| quote_id | integer or null | |
| plan_id | string or null | sp_<id> or cp_<id> on a plan visit. |
| currency | string | |
| lines | array of object | The linked invoice's (or estimate's) customer lines. Never a hidden line. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| equipment | array of object | What goes in: names and quantities only. |
| equipment[].name | string | |
| equipment[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| en_route_at | date-time or null | ISO 8601 with the business's UTC offset. |
| checked_in_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | The 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.
One job (a sales consult's id is 404 here: it is a booking).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | ap_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>: the same id GET v1/bookings uses. |
| type | string | One of: install, service_booking, service_call, plan_visit, job. |
| status | string | The Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| title | string | |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| arrival_window_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| assigned_to | object or null | Who does it. |
| assigned_to.id | integer | |
| assigned_to.name | string | |
| offer_status | string or null | The tech's answer to the offer. One of: pending, accepted, declined, null. |
| booked_by | object | |
| booked_by.id | integer | |
| booked_by.name | string | |
| job_number | string or null | |
| notes | string | |
| invoice_id | integer or null | GET v1/invoices/{id}. |
| estimate_id | integer or null | GET v1/estimates/{id}. |
| quote_id | integer or null | |
| plan_id | string or null | sp_<id> or cp_<id> on a plan visit. |
| currency | string | |
| lines | array of object | The linked invoice's (or estimate's) customer lines. Never a hidden line. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| equipment | array of object | What goes in: names and quantities only. |
| equipment[].name | string | |
| equipment[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| en_route_at | date-time or null | ISO 8601 with the business's UTC offset. |
| checked_in_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | The 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.
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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | ap_<id>. |
Body (JSON or form-encoded)
| Field | Type | Notes |
|---|---|---|
| start_at required | date-time | Date and time, in the future. |
| arrival_window | boolean | A sold install only. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>: the same id GET v1/bookings uses. |
| type | string | One of: install, service_booking, service_call, plan_visit, job. |
| status | string | The Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| title | string | |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| arrival_window_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| assigned_to | object or null | Who does it. |
| assigned_to.id | integer | |
| assigned_to.name | string | |
| offer_status | string or null | The tech's answer to the offer. One of: pending, accepted, declined, null. |
| booked_by | object | |
| booked_by.id | integer | |
| booked_by.name | string | |
| job_number | string or null | |
| notes | string | |
| invoice_id | integer or null | GET v1/invoices/{id}. |
| estimate_id | integer or null | GET v1/estimates/{id}. |
| quote_id | integer or null | |
| plan_id | string or null | sp_<id> or cp_<id> on a plan visit. |
| currency | string | |
| lines | array of object | The linked invoice's (or estimate's) customer lines. Never a hidden line. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| equipment | array of object | What goes in: names and quantities only. |
| equipment[].name | string | |
| equipment[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| en_route_at | date-time or null | ISO 8601 with the business's UTC offset. |
| checked_in_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | The 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.
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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | ap_<id>. |
| Idempotency-Key | header | string | 1–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)
| Field | Type | Notes |
|---|---|---|
| tech_id required | integer | A tech the job's seller can hand work to. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | ap_<id>: the same id GET v1/bookings uses. |
| type | string | One of: install, service_booking, service_call, plan_visit, job. |
| status | string | The Schedule board's own status. One of: unscheduled, scheduled, unconfirmed, confirmed, en_route, in_progress, completed, canceled, needs_reschedule. |
| title | string | |
| start_at | date-time or null | ISO 8601 with the business's UTC offset. |
| end_at | date-time or null | ISO 8601 with the business's UTC offset. |
| duration_min | integer or null | |
| arrival_window_min | integer or null | |
| timezone | string | |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| assigned_to | object or null | Who does it. |
| assigned_to.id | integer | |
| assigned_to.name | string | |
| offer_status | string or null | The tech's answer to the offer. One of: pending, accepted, declined, null. |
| booked_by | object | |
| booked_by.id | integer | |
| booked_by.name | string | |
| job_number | string or null | |
| notes | string | |
| invoice_id | integer or null | GET v1/invoices/{id}. |
| estimate_id | integer or null | GET v1/estimates/{id}. |
| quote_id | integer or null | |
| plan_id | string or null | sp_<id> or cp_<id> on a plan visit. |
| currency | string | |
| lines | array of object | The linked invoice's (or estimate's) customer lines. Never a hidden line. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| equipment | array of object | What goes in: names and quantities only. |
| equipment[].name | string | |
| equipment[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| en_route_at | date-time or null | ISO 8601 with the business's UTC offset. |
| checked_in_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | The 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
The business's people, by id (read-only). No plan gate.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| role | query | string | Comma-separated: owner, sales, tech, sales_tech. |
| relationship | query | string | Comma-separated: owner, team_member, seat, crew. |
| assignable | query | string | Only people a job can (true) or cannot (false) be handed to. One of: true, false. |
| active | query | string | Active (true) or switched off (false). One of: true, false. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | Use it as tech_id in jobs and the schedule. |
| name | string | |
| role | string | One of: owner, sales, tech, sales_tech. |
| role_label | string | |
| works_jobs | boolean | |
| relationship | string | One of: owner, team_member, seat, crew. |
| assignable | boolean | A job can be handed to them (POST v1/jobs, assign). |
| string or null | Only for the business's own staff; null for a tech in a dealer's shared pool. | |
| active | boolean | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One person.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Person id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | Use it as tech_id in jobs and the schedule. |
| name | string | |
| role | string | One of: owner, sales, tech, sales_tech. |
| role_label | string | |
| works_jobs | boolean | |
| relationship | string | One of: owner, team_member, seat, crew. |
| assignable | boolean | A job can be handed to them (POST v1/jobs, assign). |
| string or null | Only for the business's own staff; null for a tech in a dealer's shared pool. | |
| active | boolean | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Service plans · 2 routes
Service plans and recurring cleans, most recently changed first (read-only). Plan: business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | Comma-separated: active, paused, past_due, canceled, ended. |
| type | query | string | Comma-separated: service_plan, cleaning_plan. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | sp_<id> a service plan, cp_<id> a recurring clean. |
| type | string | One of: service_plan, cleaning_plan. |
| status | string | active, paused, canceled or ended; a recurring clean can also be past_due. |
| customer | object | |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| schedule | object | |
| schedule.frequency | string | w1–w4, m1, m2, m3, m6, m12 (service plan) or a clean's frequency (weekly, biweekly, monthly, …). |
| schedule.frequency_label | string | |
| schedule.label | string | |
| schedule.interval_days | integer | Recurring cleans only. |
| schedule.first_visit_on | date or null | |
| schedule.visit_time | string or null | HH:MM. |
| schedule.duration_min | integer or null | |
| schedule.ends | object | |
| schedule.ends.mode | string | never, after or on. |
| schedule.ends.after_visits | integer or null | |
| schedule.ends.on | date or null | |
| schedule.ends.label | string | |
| tech | object or null | |
| tech.id | integer | |
| tech.name | string | |
| currency | string | |
| price_per_visit | money string | Tax included. |
| tax_rate | string or null | Percent; null on a recurring clean. |
| lines | array of object | Empty on a recurring clean. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| autopay | boolean | Autopay on and a card saved. Never card details. |
| next_visit_on | date or null | |
| visits_made | integer | |
| notes | string | |
| source_invoice_id | integer or null | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One plan with its latest 100 visits.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | sp_<id> or cp_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | sp_<id> a service plan, cp_<id> a recurring clean. |
| type | string | One of: service_plan, cleaning_plan. |
| status | string | active, paused, canceled or ended; a recurring clean can also be past_due. |
| customer | object | |
| customer.name | string | |
| customer.contact_name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| schedule | object | |
| schedule.frequency | string | w1–w4, m1, m2, m3, m6, m12 (service plan) or a clean's frequency (weekly, biweekly, monthly, …). |
| schedule.frequency_label | string | |
| schedule.label | string | |
| schedule.interval_days | integer | Recurring cleans only. |
| schedule.first_visit_on | date or null | |
| schedule.visit_time | string or null | HH:MM. |
| schedule.duration_min | integer or null | |
| schedule.ends | object | |
| schedule.ends.mode | string | never, after or on. |
| schedule.ends.after_visits | integer or null | |
| schedule.ends.on | date or null | |
| schedule.ends.label | string | |
| tech | object or null | |
| tech.id | integer | |
| tech.name | string | |
| currency | string | |
| price_per_visit | money string | Tax included. |
| tax_rate | string or null | Percent; null on a recurring clean. |
| lines | array of object | Empty on a recurring clean. |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| autopay | boolean | Autopay on and a card saved. Never card details. |
| next_visit_on | date or null | |
| visits_made | integer | |
| notes | string | |
| source_invoice_id | integer or null | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| visits | array of object | The latest 100, newest first. |
| visits[].id | string | |
| visits[].date | date or null | |
| visits[].status | string | |
| visits[].job_id | string or null | ap_<id>: GET v1/jobs/{id}. |
| visits[].invoice_id | integer or null | |
| visits[].completed | boolean | |
| visits[].paid | boolean | |
| visits[].amount | money string | Recurring-clean visits only. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Monitoring accounts · 2 routes
The owner's alarm monitoring accounts, most recently changed first (read-only). Monitoring switched on (else 403 feature_off), then business_suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | Comma-separated: active, paused, canceled, ended. |
| past_due | query | string | Only past-due (true) or not (false). One of: true, false. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | |
| status | string | active, paused, canceled or ended. |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| monthly_amount | money string | Per month, tax included (quarterly / yearly spread over the months). |
| monthly_rmr | money string | The same before tax. |
| billing | object | |
| billing.frequency | string | |
| billing.frequency_label | string | |
| billing.amount_per_bill | money string | |
| billing.next_bill_on | date or null | |
| billing.tax_rate | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| contract | object | |
| contract.start_on | date or null | |
| contract.term_months | integer | |
| contract.term_end_on | date or null | |
| contract.auto_renew | boolean | |
| contract.month_to_month | boolean | |
| central_station | object | |
| central_station.name | string or null | |
| central_station.account_number | string or null | |
| alarmcom_customer_id | string or null | |
| panel | string or null | |
| contract_number | string or null | |
| autopay | boolean | |
| past_due | boolean | An unpaid bill older than today. |
| past_due_amount | money string | |
| days_past_due | integer | From the oldest unpaid bill. |
| unpaid_invoices | integer | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One monitoring account.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | ma_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | |
| status | string | active, paused, canceled or ended. |
| customer | object | |
| customer.name | string | |
| customer.phone | string | |
| customer.email | string | |
| customer.address | string | |
| currency | string | |
| monthly_amount | money string | Per month, tax included (quarterly / yearly spread over the months). |
| monthly_rmr | money string | The same before tax. |
| billing | object | |
| billing.frequency | string | |
| billing.frequency_label | string | |
| billing.amount_per_bill | money string | |
| billing.next_bill_on | date or null | |
| billing.tax_rate | string | |
| lines | array of object | |
| lines[].name | string | |
| lines[].quantity | string | Decimal string without trailing zeros ("2", "2.5"). |
| lines[].unit_price | money string | |
| lines[].amount | money string | |
| lines[].discount | object | Present only on a line with a customer-visible discount. |
| lines[].discount.type | string | One of: percent, amount_per_unit. |
| lines[].discount.value | money string | |
| lines[].discount.amount_off | money string | |
| lines[].discount.note | string | |
| contract | object | |
| contract.start_on | date or null | |
| contract.term_months | integer | |
| contract.term_end_on | date or null | |
| contract.auto_renew | boolean | |
| contract.month_to_month | boolean | |
| central_station | object | |
| central_station.name | string or null | |
| central_station.account_number | string or null | |
| alarmcom_customer_id | string or null | |
| panel | string or null | |
| contract_number | string or null | |
| autopay | boolean | |
| past_due | boolean | An unpaid bill older than today. |
| past_due_amount | money string | |
| days_past_due | integer | From the oldest unpaid bill. |
| unpaid_invoices | integer | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Inventory · 4 routes
Stock on hand per person, with the low-stock flag. Plan: inventory (tech tools).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| source | query | string | install_inventory or sub_dealer_stock. One of: install_inventory, sub_dealer_stock. |
| held_by | query | string | rep_<id>. |
| low_stock | query | string | true = at or under its low-stock level; false = not. One of: true, false. |
| q | query | string | Name or SKU contains. Up to 100 characters. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | inv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only). |
| source | string | One of: install_inventory, sub_dealer_stock. |
| name | string | |
| sku | string | |
| key | string | |
| product_id | integer or null | The price-list item with the same key, or null. |
| held_by | object | |
| held_by.id | string | rep_<id> (GET v1/team: the number after rep_). |
| held_by.name | string | |
| quantity_on_hand | integer | |
| low_stock_at | integer or null | The low-stock level, null when none is set. |
| is_low | boolean | A level is set and quantity_on_hand ≤ it (the app's own rule). |
| unit_price | money string | |
| currency | string | |
| adjustable | boolean | true = POST v1/inventory/{id}/adjust works (install inventory only). |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One inventory item.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | inv_<id> or sub_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | inv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only). |
| source | string | One of: install_inventory, sub_dealer_stock. |
| name | string | |
| sku | string | |
| key | string | |
| product_id | integer or null | The price-list item with the same key, or null. |
| held_by | object | |
| held_by.id | string | rep_<id> (GET v1/team: the number after rep_). |
| held_by.name | string | |
| quantity_on_hand | integer | |
| low_stock_at | integer or null | The low-stock level, null when none is set. |
| is_low | boolean | A level is set and quantity_on_hand ≤ it (the app's own rule). |
| unit_price | money string | |
| currency | string | |
| adjustable | boolean | true = POST v1/inventory/{id}/adjust works (install inventory only). |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
The install inventory's stock in / out log, newest first.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| item_id | query | string | inv_<id> (sub-dealer stock keeps no log). |
| held_by | query | string | rep_<id>. |
| reason | query | string | One reason. One of: receive, use, install, adjust, transfer, side_job. |
| since | query | string | At or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| until | query | string | Before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | |
| item_id | string | |
| item_name | string | |
| sku | string | |
| held_by | object | |
| held_by.id | string | rep_<id> (GET v1/team: the number after rep_). |
| held_by.name | string | |
| quantity_change | integer | + received, − used. |
| reason | string | One of: receive, use, install, adjust, transfer, side_job. |
| note | string | |
| quote_id | integer or null | Set when the stock went to a job on one of your own quotes. |
| customer_id | string or null | cus_q<quote id> when quote_id is set. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | inv_<id> (install inventory). |
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| quantity_change | integer | A non-zero whole number: + received, − used. Never goes below 0. -100000 to 100000. |
| set_quantity | integer | The count on hand; the API records the difference. 0 to 1000000. |
| reason | string | Default: receive when up, use when down. One of: receive, use, adjust. |
| note | string | Default "Received (API)" / "Used (API)". Up to 200 characters. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| movement (next to data) | object or null | The logged movement; null when nothing changed (the count was already right, or a decrease was clamped at 0). |
| id | string | inv_<id> install inventory (adjustable), sub_<id> sub-dealer stock (read-only). |
| source | string | One of: install_inventory, sub_dealer_stock. |
| name | string | |
| sku | string | |
| key | string | |
| product_id | integer or null | The price-list item with the same key, or null. |
| held_by | object | |
| held_by.id | string | rep_<id> (GET v1/team: the number after rep_). |
| held_by.name | string | |
| quantity_on_hand | integer | |
| low_stock_at | integer or null | The low-stock level, null when none is set. |
| is_low | boolean | A level is set and quantity_on_hand ≤ it (the app's own rule). |
| unit_price | money string | |
| currency | string | |
| adjustable | boolean | true = POST v1/inventory/{id}/adjust works (install inventory only). |
| updated_at | date-time or null | ISO 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
E-sign contracts, most recently changed first. Plan: business_suite or quoting.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | One status. One of: draft, sent, viewed, signed, canceled. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| title | string | |
| status | string | One of: draft, sent, viewed, signed, canceled. |
| template | object | |
| template.id | integer | |
| template.name | string | |
| template.version | integer | |
| customer | object | |
| customer.id | string or null | cus_s… (from an estimate / invoice) or cus_q… (from a quote), else null. |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate, invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| rep_id | integer | |
| door_to_door | boolean | |
| cancel_by | date or null | The cancel-by date of a door-to-door sale. |
| countersign_required | boolean | |
| countersigned_at | date-time or null | ISO 8601 with the business's UTC offset. |
| view_count | integer | |
| document_id | integer or null | Its Documents entry once signed. |
| signed_pdf_url | string or null | Signed contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| first_viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_by | string or null |
Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
Make a DRAFT contract from a template and one record (the app's New contract and its auto-fill). Nothing is sent.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| template_id required | integer | A current template (GET v1/contract-templates). |
| estimate_id | integer | |
| invoice_id | integer | |
| quote_id | integer | An alarm quote. |
| monitoring_account_id | integer | The number of an ma_<id>. |
| customer_id | string | cus_…: fills from that customer's records. |
| customer | object | Someone not in FieldStacker yet: at least a name, phone or email. |
| customer.name | string | Up to 120 characters. |
| customer.phone | string | Up to 40 characters. |
| customer.email | Up to 160 characters. | |
| customer.address | string | Up to 300 characters. |
Answer 201
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| title | string | |
| status | string | One of: draft, sent, viewed, signed, canceled. |
| template | object | |
| template.id | integer | |
| template.name | string | |
| template.version | integer | |
| customer | object | |
| customer.id | string or null | cus_s… (from an estimate / invoice) or cus_q… (from a quote), else null. |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate, invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| rep_id | integer | |
| door_to_door | boolean | |
| cancel_by | date or null | The cancel-by date of a door-to-door sale. |
| countersign_required | boolean | |
| countersigned_at | date-time or null | ISO 8601 with the business's UTC offset. |
| view_count | integer | |
| document_id | integer or null | Its Documents entry once signed. |
| signed_pdf_url | string or null | Signed contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| first_viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_by | string or null |
Errors: 400 · 409 · 422 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
One contract.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Contract id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| title | string | |
| status | string | One of: draft, sent, viewed, signed, canceled. |
| template | object | |
| template.id | integer | |
| template.name | string | |
| template.version | integer | |
| customer | object | |
| customer.id | string or null | cus_s… (from an estimate / invoice) or cus_q… (from a quote), else null. |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate, invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| rep_id | integer | |
| door_to_door | boolean | |
| cancel_by | date or null | The cancel-by date of a door-to-door sale. |
| countersign_required | boolean | |
| countersigned_at | date-time or null | ISO 8601 with the business's UTC offset. |
| view_count | integer | |
| document_id | integer or null | Its Documents entry once signed. |
| signed_pdf_url | string or null | Signed contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| first_viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_by | string or null |
Errors: 404 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
⚠️ 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
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Contract id. |
| Idempotency-Key | header | string | 1–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
| Field | Type | Notes |
|---|---|---|
| channel | string | email = ✉️ 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:
| Field | Type | Notes |
|---|---|---|
| sent (next to data) | object | |
| id | integer | |
| title | string | |
| status | string | One of: draft, sent, viewed, signed, canceled. |
| template | object | |
| template.id | integer | |
| template.name | string | |
| template.version | integer | |
| customer | object | |
| customer.id | string or null | cus_s… (from an estimate / invoice) or cus_q… (from a quote), else null. |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate, invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| rep_id | integer | |
| door_to_door | boolean | |
| cancel_by | date or null | The cancel-by date of a door-to-door sale. |
| countersign_required | boolean | |
| countersigned_at | date-time or null | ISO 8601 with the business's UTC offset. |
| view_count | integer | |
| document_id | integer or null | Its Documents entry once signed. |
| signed_pdf_url | string or null | Signed contracts only: the lasting link the signer and you were emailed. Anyone with it can open the signed copy. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| sent_at | date-time or null | ISO 8601 with the business's UTC offset. |
| first_viewed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_at | date-time or null | ISO 8601 with the business's UTC offset. |
| canceled_by | string or null |
Errors: 400 · 404 · 409 · 422 · 502 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
Contract templates · 1 route
Your current contract templates, by id (read-only).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | template_id for POST v1/contracts. |
| name | string | |
| version | integer | |
| door_to_door | boolean | |
| cancel_days | integer | |
| countersign_required | boolean | |
| fields | array of object | |
| fields[].key | string | |
| fields[].kind | string | |
| fields[].label | string | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
Documents · 2 routes
The Documents page (signed contracts), newest signed first (read-only).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| kind | query | string | contract. One of: contract. |
| status | query | string | signed or canceled. One of: signed, canceled. |
| from | query | string | Signed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Signed before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| kind | string | contract. |
| status | string | signed or canceled. |
| title | string | |
| template_name | string | |
| customer | object | |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate_or_invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| contract_id | integer or null | |
| rep_id | integer | |
| doc_hash | string or null | SHA-256 of the signed words. |
| download_url | string or null | The Documents page's own download: opens only for someone signed in to FieldStacker who may see it. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 503 · 403 (plus 401, 429, 500 on every route). What they mean.
One document.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Document id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| kind | string | contract. |
| status | string | signed or canceled. |
| title | string | |
| template_name | string | |
| customer | object | |
| customer.name | string | |
| customer.email | string | |
| customer.phone | string | |
| customer.address | string | |
| source | object | |
| source.type | string or null | estimate_or_invoice, quote, monitoring_account, customer or blank. |
| source.id | integer or null | |
| contract_id | integer or null | |
| rep_id | integer | |
| doc_hash | string or null | SHA-256 of the signed words. |
| download_url | string or null | The Documents page's own download: opens only for someone signed in to FieldStacker who may see it. |
| signed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Notes · 2 routes
A lead's or a customer's notes (send lead_id OR customer_id). Customer notes need a plan with Customers.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| lead_id | query | integer | A lead (pin) id. |
| customer_id | query | string | A cus_… id. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| subject (next to data) | object | |
| id | string | ln_<id> a lead's History entry, cn_<id> a customer's note. |
| type | string | One of: note, voice_note. |
| text | string | |
| rep_id | integer or null | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 404 · 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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.
| Field | Type | Notes |
|---|---|---|
| lead_id | integer | |
| customer_id | string | cus_…. |
| text required | string | The line to add. Up to 500 characters. |
Answer 201
A list: data is an array of these (with next_cursor and limit).
| Field | Type | Notes |
|---|---|---|
| subject (next to data) | object | |
| id | string | ln_<id> a lead's History entry, cn_<id> a customer's note. |
| type | string | One of: note, voice_note. |
| text | string | |
| rep_id | integer or null | |
| created_at | date-time or null | ISO 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
Photo metadata, newest first (read-only). Pin photos need the knock map, job photos estimates and invoices; otherwise they are left out.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| source | query | string | media, pin or job. One of: media, pin, job. |
| lead_id | query | integer | |
| job_id | query | integer | An estimate / invoice id. |
| since | query | string | Created after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | med_<id> Photos hub, pin_<id> a pin's house photo, job_<id> an estimate / invoice photo. |
| source | string | One of: media, pin, job. |
| kind | string | One of: photo, video, audio. |
| mime | string or null | |
| caption | string | |
| tag | string | A pin photo's tag, or before / after on a job photo. |
| lead_id | integer or null | |
| job_id | integer or null | The estimate / invoice id. |
| customer_name | string | |
| address | string | |
| rep_id | integer or null | |
| width | integer or null | |
| height | integer or null | |
| view_url | string or null | The app's own sign-in-gated route; there is no public photo link and the API serves no files. |
| captured_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One photo's metadata.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | string | med_<id>, pin_<id> or job_<id>. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | string | med_<id> Photos hub, pin_<id> a pin's house photo, job_<id> an estimate / invoice photo. |
| source | string | One of: media, pin, job. |
| kind | string | One of: photo, video, audio. |
| mime | string or null | |
| caption | string | |
| tag | string | A pin photo's tag, or before / after on a job photo. |
| lead_id | integer or null | |
| job_id | integer or null | The estimate / invoice id. |
| customer_name | string | |
| address | string | |
| rep_id | integer or null | |
| width | integer or null | |
| height | integer or null | |
| view_url | string or null | The app's own sign-in-gated route; there is no public photo link and the API serves no files. |
| captured_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Reviews · 2 routes
Star reviews on your digital cards, newest first (read-only).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| since | query | string | Created after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| min_rating | query | integer | 1–5. 1 to 5. |
| hidden | query | string | Only hidden (true) or shown (false) reviews. One of: true, false. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| source | string | One of: card. |
| rating | integer | 1 to 5. |
| reviewer_name | string | |
| text | string | |
| hidden | boolean | Hidden from the card in the app. |
| rep_id | integer | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One review.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Review id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| source | string | One of: card. |
| rating | integer | 1 to 5. |
| reviewer_name | string | |
| text | string | |
| hidden | boolean | Hidden from the card in the app. |
| rep_id | integer | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Knocks · 2 routes
Door-knock history (worked doors), most recently changed first (read-only). Plan: the knock map.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| from | query | string | Knocked at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Knocked before (a date = the end of that day). ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| status | query | string | One disposition key. |
| rep_id | query | integer | Who knocked it. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | The pin id (also works with GET v1/leads/{id}). |
| lead_id | integer | |
| address | string | |
| lat | number or null | |
| lng | number or null | |
| status | string | Your disposition key. |
| status_label | string | Your label for it. |
| rep_id | integer | Who knocked it (else the assignee, else the pin's owner). |
| is_business | boolean | |
| verified | boolean | The rep's phone was at the door. |
| first_knocked_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_knocked_at | date-time or null | ISO 8601 with the business's UTC offset. |
| won_at | date-time or null | ISO 8601 with the business's UTC offset. |
| appointment_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One worked door with its visits.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Pin id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | The pin id (also works with GET v1/leads/{id}). |
| lead_id | integer | |
| address | string | |
| lat | number or null | |
| lng | number or null | |
| status | string | Your disposition key. |
| status_label | string | Your label for it. |
| rep_id | integer | Who knocked it (else the assignee, else the pin's owner). |
| is_business | boolean | |
| verified | boolean | The rep's phone was at the door. |
| first_knocked_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_knocked_at | date-time or null | ISO 8601 with the business's UTC offset. |
| won_at | date-time or null | ISO 8601 with the business's UTC offset. |
| appointment_at | date-time or null | ISO 8601 with the business's UTC offset. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| visits | array of object | The pin's disposition changes, newest first. |
| visits[].change | string | e.g. "Not home → Callback". |
| visits[].rep_id | integer or null | |
| visits[].at_the_door | boolean or null | |
| visits[].at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Campaigns · 3 routes
Marketing campaigns with counts, most recently changed first (read-only). Plan: the marketing suite.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| status | query | string | One status. One of: active, paused, draft, archived. |
| updated_since | query | string | Changed at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| name | string | |
| type | string or null | |
| status | string | active, paused, draft or archived. |
| service | string | |
| source_label | string | |
| goal | string or null | |
| rep_id | integer | |
| landing_url | string or null | The public page printed on its flyers and postcards. |
| stats | object | |
| stats.sends | integer | Postcards mailed (test sends left out). |
| stats.opens | integer | Mailed households who scanned. |
| stats.scans | integer | Every scan. |
| stats.visits | integer | Landing-page visits. |
| stats.replies | integer | Leads it brought in. |
| stats.bookings | integer | Those leads with a booked visit. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| archived_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
One campaign.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Campaign id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| name | string | |
| type | string or null | |
| status | string | active, paused, draft or archived. |
| service | string | |
| source_label | string | |
| goal | string or null | |
| rep_id | integer | |
| landing_url | string or null | The public page printed on its flyers and postcards. |
| stats | object | |
| stats.sends | integer | Postcards mailed (test sends left out). |
| stats.opens | integer | Mailed households who scanned. |
| stats.scans | integer | Every scan. |
| stats.visits | integer | Landing-page visits. |
| stats.replies | integer | Leads it brought in. |
| stats.bookings | integer | Those leads with a booked visit. |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| archived_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Your marketing in one answer: campaigns, direct mail, lit-up postcards.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| from | query | string | At or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| to | query | string | Before (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:
| Field | Type | Notes |
|---|---|---|
| from | date-time or null | ISO 8601 with the business's UTC offset. |
| to | date-time or null | ISO 8601 with the business's UTC offset. |
| campaigns | object | |
| campaigns.count | integer | |
| campaigns.visits | integer | |
| campaigns.replies | integer | |
| direct_mail | object | Test sends and canceled mailings left out. |
| direct_mail.mailings | integer | |
| direct_mail.sends | integer | |
| direct_mail.failed | integer | |
| direct_mail.opens | integer | |
| direct_mail.scans | integer | |
| lit_up_postcards | object | |
| lit_up_postcards.sends | integer | |
| lit_up_postcards.opens | integer | |
| lit_up_postcards.replies | integer |
Errors: 400 · 403 (plus 401, 429, 500 on every route). What they mean.
Webhooks & tools
Your account (me) · 1 route
The business this key belongs to (a connection test).
Answer 200
| Field | Type | Notes |
|---|---|---|
| ok | true | |
| company_id | integer | |
| company_name | string | |
| owner_name | string | |
| timezone | string | IANA zone, e.g. America/Los_Angeles. |
| api_version | string | |
| events | array of string | Every 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
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).
| Field | Type | Notes |
|---|---|---|
| max_webhooks (next to data) | integer | |
| id | integer | |
| label | string | |
| url | uri | https on a public host. |
| events | array of string | |
| status | string | One of: active, paused. |
| paused_reason | string or null | owner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null. |
| paused_at | date-time or null | ISO 8601 with the business's UTC offset. |
| failure_streak | integer | Failed attempts in a row. |
| last_delivered_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_failed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| secret_hint | string or null | The 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_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 403 (plus 401, 429, 500 on every route). What they mean.
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
| Name | In | Type | Notes |
|---|---|---|---|
| Idempotency-Key | header | string | 1–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)
| Field | Type | Notes |
|---|---|---|
| url required | uri | https on a public host (no private, loopback, link-local or reserved address; checked again at every send). Up to 600 characters. |
| events required | array of string or string | |
| label | string | Defaults to the URL's host. Up to 80 characters. |
| paused | boolean | Default false. |
Answer 201
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| label | string | |
| url | uri | https on a public host. |
| events | array of string | |
| status | string | One of: active, paused. |
| paused_reason | string or null | owner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null. |
| paused_at | date-time or null | ISO 8601 with the business's UTC offset. |
| failure_streak | integer | Failed attempts in a row. |
| last_delivered_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_failed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| secret_hint | string or null | The 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_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| secret | string or null | The 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.
One webhook.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Webhook id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| label | string | |
| url | uri | https on a public host. |
| events | array of string | |
| status | string | One of: active, paused. |
| paused_reason | string or null | owner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null. |
| paused_at | date-time or null | ISO 8601 with the business's UTC offset. |
| failure_streak | integer | Failed attempts in a row. |
| last_delivered_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_failed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| secret_hint | string or null | The 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_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Change its label, URL or events, or pause / resume it.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Webhook id. |
Body (JSON or form-encoded)
Send at least one field. Only what is sent changes.
| Field | Type | Notes |
|---|---|---|
| url | uri | Up to 600 characters. |
| events | array of string or string | |
| label | string | Up to 80 characters. |
| paused | boolean | true pauses (queued deliveries are held); false resumes (held deliveries younger than 3 days are sent). |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| label | string | |
| url | uri | https on a public host. |
| events | array of string | |
| status | string | One of: active, paused. |
| paused_reason | string or null | owner = paused by you; auto = kept failing; gone = the receiver answered 410. One of: owner, auto, gone, null. |
| paused_at | date-time or null | ISO 8601 with the business's UTC offset. |
| failure_streak | integer | Failed attempts in a row. |
| last_delivered_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_failed_at | date-time or null | ISO 8601 with the business's UTC offset. |
| secret_hint | string or null | The 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_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 404 · 409 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
Delete it. Its queued deliveries are dropped; the log keeps the finished ones.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Webhook id. |
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| id | integer | |
| deleted | true |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Send a signed test event ("test": true) to it now. Works while paused; never retried. At most 5 a minute.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| id required | path | integer | Webhook id. |
| Idempotency-Key | header | string | 1–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
| Field | Type | Notes |
|---|---|---|
| event | string | Default: 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:
| Field | Type | Notes |
|---|---|---|
| webhook_id | integer | |
| event | string | |
| delivered | boolean | The receiver answered 2xx. |
| status_code | integer or null | |
| error | string or null | |
| response_snippet | string | The receiver's answer, cleaned, ≤300 characters. |
Errors: 400 · 404 · 422 · 403 (plus 401, 429, 500 on every route). What they mean.
The delivery log (30 days), newest first.
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| webhook_id | query | integer | Only this webhook. |
| event | query | string | Only 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. |
| status | query | string | Comma-separated: delivered, failed, retrying, queued, sending, held, skipped. |
| since | query | string | Queued at or after. ISO 8601 (no offset = the business's local time), YYYY-MM-DD or unix seconds. |
| limit | query | integer | 1–100. 1 to 100. Default 50. |
| cursor | query | string | next_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).
| Field | Type | Notes |
|---|---|---|
| id | string | dlv_…: the X-FieldStacker-Delivery header (unchanged across retries). |
| event | string | |
| event_id | string or null | evt_…: the body's id. |
| test | boolean | |
| webhook_id | integer or null | null for a note row that was never sent. |
| webhook_label | string | The label when it was queued. |
| status | string | One of: delivered, failed, retrying, queued, sending, held, skipped. |
| attempts | integer | |
| max_attempts | integer | |
| will_retry | boolean | |
| next_attempt_at | date-time or null | ISO 8601 with the business's UTC offset. |
| last_status_code | integer or null | |
| last_error | string or null | |
| response_snippet | string | |
| duration_ms | integer or null | |
| created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| updated_at | date-time or null | ISO 8601 with the business's UTC offset. |
| completed_at | date-time or null | ISO 8601 with the business's UTC offset. |
Errors: 400 · 404 · 403 (plus 401, 429, 500 on every route). What they mean.
Events · 2 routes
Every event a webhook can subscribe to.
Answer 200
A list: data is an array of these (with next_cursor and limit).
| Field | Type | Notes |
|---|---|---|
| api_version (next to data) | string | |
| key | string | 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. |
| name | string | |
| description | string |
Errors: 403 (plus 401, 429, 500 on every route). What they mean.
One event type + a sample payload (nothing is sent).
Parameters
| Name | In | Type | Notes |
|---|---|---|---|
| event required | path | string | An 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:
| Field | Type | Notes |
|---|---|---|
| key | string | 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. |
| name | string | |
| description | string | |
| headers | array of string | |
| sample | object | A 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
This key's rate-limit window and request counts (read-only).
Answer 200
data is:
| Field | Type | Notes |
|---|---|---|
| key | object | |
| key.prefix | string | The first 10 characters ("fsk_1a2b3c"). |
| key.created_at | date-time or null | ISO 8601 with the business's UTC offset. |
| key.last_used_at | date-time or null | ISO 8601 with the business's UTC offset. |
| rate_limit | object | |
| rate_limit.limit | integer | Requests per key per window. |
| rate_limit.window_seconds | integer | |
| rate_limit.used | integer | In the current window, this call included. |
| rate_limit.remaining | integer | |
| rate_limit.resets_at | date-time or null | When the oldest call in the window ages out. ISO 8601 with the business's UTC offset. |
| requests | object | Every count includes this call. |
| requests.last_hour | integer | |
| requests.today | integer | Since local midnight. |
| requests.last_24_hours | integer | |
| requests.last_30_days | integer | |
| requests.errors_last_24_hours | integer | Status 400 and up. |
| requests.rate_limited_last_24_hours | integer | Status 429. |
| by_day | array of object | Newest first, local dates, days with calls only (this call not included). |
| by_day[].date | date | |
| by_day[].requests | integer | |
| by_day[].errors | integer | |
| by_day[].rate_limited | integer | |
| retention_days | integer | |
| timezone | string |
Errors: 404 · 403 (plus 401, 429, 500 on every route). What they mean.
OpenAPI description · 1 route
This description. No key needed.
Answer 200
An OpenAPI 3.1 document.
Questions or a bug? Email support@fieldstacker.com.