Version 2026-10-14. Read and write customers, locations, jobs, dispatch, estimates, invoices, payments, the price book and technicians; get a signed webhook for every lifecycle event. Everything you can do here goes through the same rules, permissions and audit trail as the office app.
Authentication
Create an API key in Settings › Integrations › API Application Access (owners and admins) and pick its scopes. Send it as a bearer token. Apps that other companies install use OAuth access tokens instead (below).
Read the demand forecast and demand requests (fill the board, throttle ads)
demand:respond
Report campaigns launched or declined for demand requests
customers:write
Create customers and service locations
jobs:write
Create jobs
dispatch:write
Schedule, assign and move appointments through their statuses (dispatch, arrive, complete)
estimates:read
Read estimates
invoices:read
Read invoices and payments
payments:write
Record payments and create card payment links
pricebook:read
Read the price book
technicians:read
Read the technician list
webhooks:manage
Subscribe to webhooks (Zapier, n8n and other REST hooks)
leads:write
Send new leads to the lead inbox (answered by the text agent like any other lead)
marketing.read
Read campaigns, campaign performance, lead status, conversions, audience counts, capacity and review summaries
marketing.write
Create and update campaigns, record ad spend and send in new leads
booking.write
Book jobs and appointments (creates customers and locations)
audiences.export
Export hashed contact lists of customers who agreed to marketing (only if an owner turns it on)
Conventions
Base URL https://tradesos-three.vercel.app/api/v1/platform. JSON in and out. Money is integer cents; times are ISO-8601.
Lists are newest first: limit (1–100), then pass nextCursor as cursor. Sync with updatedSince; find imported records with externalSource + externalId.
Every POST that creates something accepts an Idempotency-Key header: a retry with the same key and body returns the first response (header Idempotent-Replayed: true) for 24 hours.
Rate limit: 120 requests per 60 seconds per credential. Watch X-RateLimit-Remaining; a 429 carries Retry-After.
Errors: { "error": "plain words", "code": "invalid | unauthenticated | insufficient_scope | forbidden | not_found | rejected | rate_limited | …", "requestId": "…" }. Another company’s id is always a 404.
Endpoints
Account
GET
/me
The company and access behind this credential
any
Customers
GET
/customers
List customers
customers:read
GET
/customers/{id}
Get a customer
customers:read
POST
/customers
Create a customer (optionally with a first location)
customers:write
Locations
GET
/locations
List service locations
customers:read
GET
/locations/{id}
Get a location
customers:read
POST
/customers/{id}/locations
Add a service location to a customer
customers:write
Jobs
GET
/jobs
List jobs
jobs:read
GET
/jobs/{id}
Get a job
jobs:read
POST
/jobs
Book a job for an existing customer and location
Lands unscheduled for the dispatcher. Idempotent on externalRef and on the Idempotency-Key header.
jobs:write
Appointments
GET
/appointments
List appointments
jobs:read
GET
/appointments/{id}
Get an appointment
jobs:read
Dispatch
POST
/appointments
Schedule an appointment on a job (arrival window, technicians)
dispatch:write
POST
/appointments/{id}/assign
Assign technicians (replaces the crew)
dispatch:write
POST
/appointments/{id}/status
Move an appointment: dispatched, en_route, working (arrived), done, canceled
Repeating the current status is a no-op. Completing needs an outcome and summary, and the company's required forms and photos.
dispatch:write
Estimates
GET
/estimates
List estimates (one per option)
estimates:read
GET
/estimates/{id}
Get an estimate
estimates:read
Invoices
GET
/invoices
List invoices
invoices:read
GET
/invoices/{id}
Get an invoice
invoices:read
Payments
POST
/invoices/{id}/payments
Record a cash, check or other payment
Paid status follows payments. Use the Idempotency-Key header so a retry records the payment once.
payments:write
POST
/invoices/{id}/checkout
Create a card payment link for the balance
409 processor_unavailable when no card processor is connected (never a fake link).
payments:write
GET
/payments
List payments
invoices:read
GET
/payments/{id}
Get a payment
invoices:read
Price book
GET
/pricebook/items
List price book items
pricebook:read
GET
/pricebook/items/{id}
Get a price book item
pricebook:read
Technicians
GET
/technicians
People who can be assigned to appointments
technicians:read
Webhooks
GET
/webhooks/event-types
Every event type a webhook can receive
any
GET
/webhooks
Webhook endpoints this credential created
webhooks:manage
POST
/webhooks
Subscribe (REST hook: Zapier, n8n, Make)
Returns the signing secret once. Event types are limited to what this credential may read.
webhooks:manage
DELETE
/webhooks/{id}
Unsubscribe
webhooks:manage
POST
/webhooks/{id}/test
Send a signed ping event now
webhooks:manage
Full request and response schemas: /api/v1/openapi.json (OpenAPI 3.1; import it into Postman, Insomnia or a client generator).
Webhooks
Add an endpoint in Settings › Integrations › API Application Access (or subscribe through the API). Within about a minute of a change we POST an event. Respond with any 2xx within 10 seconds. Anything else is retried after 1 min, 5 min, 30 min, 2 h, 8 h, 24 h, then marked failed; you can resend it from the delivery log. An endpoint that fails continuously for 3 days is turned off and the owner sees why.
Verify every delivery: compute HMAC-SHA256 of `${t}.${rawBody}` with your endpoint’s signing secret and compare it (constant time) with each v1. Reject timestamps more than 5 minutes old, and skip event ids you have already processed (deliveries can repeat). During a secret rotation two v1 values are sent for 24 hours.
Appointment status changed (dispatched, en route, arrived, done, canceled)
appointment
estimate.created
Estimate created
estimate
estimate.sold
Estimate sold
estimate
estimate.dismissed
Estimate dismissed
estimate
invoice.created
Invoice created
invoice
invoice.sent
Invoice sent
invoice
payment.recorded
Payment recorded (cash, check, other)
payment
payment.succeeded
Card or ACH payment succeeded
payment
payment.failed
Card or ACH payment failed
payment
payment.refunded
Refund issued
invoice
membership.sold
Membership sold
membership
membership.renewed
Membership renewed
membership
membership.canceled
Membership canceled
membership
membership.expired
Membership expired
membership
lead.received
Lead received
lead
lead.converted
Lead converted to a job
lead
pricebook_item.created
Price book item created
pricebook_item
pricebook_item.updated
Price book item updated
pricebook_item
Zapier, n8n and Make
Use REST hooks: with a key that has webhooks:manage, POST /webhooks with the catch URL and event types to subscribe, and DELETE /webhooks/{id} to unsubscribe. A key only receives events for records its scopes can read. Actions map to the POST endpoints (create customer, book job, schedule, record payment).
Register your app in Settings › Integrations › API Application Access to get a client id and secret. Authorization code flow with PKCE (S256) is required; access tokens last one hour, refresh tokens rotate on every use, and a replayed code ends the grant. The app never gets more than the approving owner or admin can do.
1. Send the person to:
https://tradesos-three.vercel.app/oauth/authorize?response_type=code&client_id=tosapp_…&redirect_uri=https://you/cb
&scope=jobs:read%20invoices:read&state=…&code_challenge=…&code_challenge_method=S256
2. Exchange the code:
curl -X POST https://tradesos-three.vercel.app/api/oauth/token -u tosapp_…:tossec_… \
-d grant_type=authorization_code -d code=… -d redirect_uri=https://you/cb -d code_verifier=…
3. Refresh: -d grant_type=refresh_token -d refresh_token=tosrt_…
4. Revoke: curl -X POST https://tradesos-three.vercel.app/api/oauth/revoke -u tosapp_…:tossec_… -d token=…