A map for software, and for the AI

Ask the desk.

Look up a customer, price the job, find a Thursday, send the text. Same commands the office already uses. A key lets your own tools do it too.

The HTTP API is part of the AI plan ($99 a month). Mint a key in the app under Settings → ChalkBot (API keys). Core ($49) cannot mint or use developer keys. Signing into the app is not this API. The website estimator ingest key is a different thing and stays on every plan.

How a call works

Same shop the crew already uses. A key names the business, a scope says how loud it can be, and quiet hours still bind. Nothing here invents a second CRM.

1

A key for the shop

Mint a key in Settings → ChalkBot (API keys). AI plan ($99) only. It belongs to the business, not to a person signed into a browser. Revoke it and the calls stop.

2

A scope for the job

read looks. write files the work. send texts a customer. files handles photos. Start tight.

3

A command you already know

The paths below are the ones the app already runs. Your software hits the same commands the office does.

GET https://app.chalkcrm.com/api/clients/lookup?q=thompson
Authorization: Bearer chk_live_…

Mint that Bearer token in Settings → ChalkBot (API keys). AI plan only.

Four scopes on the key

A Zapier key can read the board without ever sending a text. The loudest scope is send, and even then quiet hours and your channels still apply.

read

Look up customers, quotes, the schedule, invoices, the inbox and the dashboard. Default on a new key.

write

Create and edit customers, quotes, jobs, visits, invoices, notes and the price book. Nothing leaves the shop yet.

send

Text or email a quote, an invoice, a booking, an on-my-way or a reminder. The one that talks to a customer.

files

Upload and download job photos. Separate so a calendar key does not need the gallery.

The commands

What each call does

Plain paths, same as the app. Merge, refunds, payroll and charging a card on file stay in the office, on purpose. You can archive an invoice, or delete an unpaid one with no payments.

Website leads (live now)

Mint it under Settings, Connectors. It files a lead from your own site. It is on every plan, including Core. It is not the $99 developer API, and Core shops keep this one even though they cannot mint developer keys.

POST/api/ingest/lead

Send first_name plus a phone or email, with the key in X-Api-Key.

Customers

Find or create the person before a quote, a lead or a text. The lookup is the one you want from a chat.

GET/api/clients

Optional ?q= filters the book (name, company, email, phone digits, street). Omit q for the unfiltered list (capped).

GET/api/clients/lookup

Query q (name, phone fragment, address). Examples: ?q=John ?q=Ball ?q=864. Also still accepts name and phone for the in-app duplicate check. Returns { matches: [...] }.

GET/api/clients/:id

One customer, with properties and recent work.

POST/api/clients

Add a customer.

PATCH/api/clients/:id

Update a name, notes, type or terms.

POST/api/clients/:id/phones

Add a phone number.

POST/api/clients/:id/emails

Add an email address.

POST/api/clients/:id/portal-link

Hand them their portal without opening the app.

Properties

The job address. Quotes and visits hang off it, not off the person.

GET/api/properties

Addresses on file for the shop.

GET/api/properties/suggest

Address suggestions as you type.

POST/api/properties

Add a property to a customer.

PATCH/api/properties/:id

Fix an address, gate note or geo.

Leads

The funnel from first enquiry to a quote. Move a card, or turn a yes into an estimate.

GET/api/requests

Leads on the board.

GET/api/requests/pipeline

The funnel columns, with cards in each.

GET/api/requests/:id

One lead, with the thread around it.

POST/api/requests

File a new enquiry.

PATCH/api/requests/:id

Move it, rename it, change the stage.

POST/api/requests/:id/convert

Turn a yes into a quote.

Quotes

The whole estimate loop: draft from the price book, send the link, watch them sign, turn it into a job. A measured line is quantity on a price-book measure. Example: 480 sq ft, charge 350, cost you 120, crew pay 100, all cents. Shop must have measurements on. POST and PATCH lines can include measures[] so cost you per sq ft and crew pay per sq ft work.

GET/api/quotes

Estimates in the shop.

GET/api/quotes/:id

One quote, with lines, extras and line_item_measures.

GET/api/quotes/:id/materials

What the job will take, from the lines.

GET/api/quotes/:id/pdf

The PDF the customer sees.

POST/api/quotes

Draft with line_items[]. Each line may include product_id and measures[] (product_measure_id, quantity, optional unit_price_cents). Shop must have measurements on. Cost you and crew pay copy from the price book.

PATCH/api/quotes/:id

Same nested lines, including measures[]. Change extras, deposit or terms.

POST/api/quotes/:id/send

Body { email: true, sms: true } to send both. Empty body uses the shop default (email) and 400s if the client has no email. { email: false, sms: true } is SMS-only. Office also accepts { channel, to, message }. Uses tpl_quote_sms for the body and tpl_quote_email_subject for the email subject. Needs send.

POST/api/quotes/:id/link

Mint the public link without sending it. Returns { hub_url, app_url }. hub_url is the customer portal. app_url is the office page https://app.chalkcrm.com/quotes/:id.

POST/api/quotes/:id/approve

Mark it signed from the office.

POST/api/quotes/:id/convert

Turn an approved quote into a job.

POST/api/quotes/:id/duplicate

Copy last year's deck job as a starting point.

POST/api/quotes/:id/archive

Take it off the live list.

POST/api/quotes/:id/unarchive

Put it back.

Jobs

Won work. Finish it, then bill it from the same record.

GET/api/jobs

Jobs on the books.

GET/api/jobs/:id

One job, with visits and invoices.

POST/api/jobs

Open a job without a quote, if you need to.

PATCH/api/jobs/:id

Change crew, notes or status.

POST/api/jobs/:id/complete

Mark the work done.

POST/api/jobs/:id/invoice

Turn the finished job into an invoice.

POST/api/jobs/:id/archive

Take it off the live list.

POST/api/jobs/:id/unarchive

Put it back.

POST/api/jobs/preview-recurrence

See the next visits before you lock a series.

Schedule

Book, move and confirm visits. This is the core of the desk running itself.

GET/api/schedule

The calendar for a range of days.

GET/api/schedule/my-day

What a crew member is running today.

GET/api/schedule/blackouts

Days the shop is closed. Do not book over them.

POST/api/schedule/find-time

Ask for the next open slot that fits.

POST/api/schedule/check

See if a time collides before you book it.

POST/api/schedule/visits

Put a visit on the board.

PATCH/api/schedule/visits/:id

Move it, reassign it, change the window.

POST/api/schedule/visits/:id/confirm

Tell the customer they are on the board. Needs send.

POST/api/schedule/visits/:id/on-my-way

Text that the crew is rolling. Needs send.

POST/api/schedule/visits/:id/complete

The stop is done.

POST/api/schedule/series

Book a repeating visit.

POST/api/schedule/optimize

Reorder the day's stops.

Invoices and money

Bill, chase and see what landed. Recording a check is in. You can archive an invoice, or delete an unpaid one with no payments. Paid invoices archive. They do not delete. Charging a saved card, refunds and write-offs stay in the app.

GET/api/invoices

Invoices in the shop.

GET/api/invoices/aging

Who is late, for chase copy.

GET/api/invoices/:id

One invoice, with payments.

GET/api/invoices/:id/pdf

The bill as a PDF.

GET/api/invoices/:id/receipt.pdf

The receipt after it is paid.

POST/api/invoices

Write an invoice by hand.

PATCH/api/invoices/:id

Change lines or due date while it is still a draft.

POST/api/invoices/:id/send

Send the pay link. Needs send.

POST/api/invoices/remind

Nudge the slow ones. Needs send.

POST/api/invoices/:id/link

Mint the pay link without sending it.

POST/api/invoices/:id/archive

Take it off the live list. Paid invoices archive. They do not delete.

POST/api/invoices/:id/unarchive

Put it back.

DELETE/api/invoices/:id

Delete an unpaid invoice with no payments. If money landed, archive it instead.

GET/api/payments

Money that has come in.

GET/api/payments/in-transit

Card payments still on the way to the bank.

POST/api/payments

Record a check or cash the owner was just told about.

GET/api/payments/cards/:clientId

See that a card is on file. Charging it is a person in the loop.

Messages

The business number, as an inbox. Replies land here. Sends still respect quiet hours and whether text or email is actually wired.

GET/api/messages

Threads in the inbox.

GET/api/messages/thread

The back and forth with one customer.

GET/api/messages/unread

What has not been opened yet.

GET/api/messages/recipients

Who you can text from here.

POST/api/messages/send

Reply from the business number. Needs send.

POST/api/messages/read

Mark a thread read.

GET/api/connectors/channels

Whether text, email or neither can actually send.

Notes and photos

Gate codes, dog names, call before you come. Before and after shots live on the job. line_items[].description on a quote is the printed line, not an office note. Office notes are GET /api/notes/for-client/:id and POST /api/notes with a message. They are different on purpose.

GET/api/notes/for-client/:id

Notes on a customer.

POST/api/notes

Write a note on a customer, job or quote. Put the message in body (or message). Quote line descriptions stay on the quote.

PATCH/api/notes/:id

Fix a note.

PUT/api/files

Query entity_type, entity_id, optional filename. Body is raw bytes, Content-Type: image/jpeg (not JSON). JSON {entity_type, entity_id} is wrong. There is no GET /api/files index (404). List is GET /api/files/list/:type/:id. Fetch one is GET /api/files/:id. Needs files.

GET/api/files/gallery

The shop gallery, searchable. Needs files.

GET/api/files/list/:type/:id

Photos on one job or quote.

GET/api/files/:id

Fetch one file.

Search and the board

Find Thompson's fence job from a sentence. See what is open today.

GET/api/search

Search customers, quotes, jobs and invoices.

GET/api/dashboard/search

The same hunt the header bar uses.

GET/api/dashboard

What is open: leads, quotes waiting, jobs, money owed.

GET/api/dashboard/reports

The numbers, read only.

GET/api/dashboard/referrals

Who sent work, and what those people earned.

GET/api/users

Who can be assigned. Creating users stays in the app.

GET/api/notifications

What the office bell is holding.

GET/api/notifications/prefs

Which bells this person wants: leads, approvals, ChalkBot, and the rest of the catalog.

PUT/api/notifications/prefs

Turn those types on or off.

ChalkBot

The meter and the minutes. Asking the assistant still happens in the app. Buying extra credits stays in Settings.

GET/api/assistant/usage

Credits used this week, the included 250, and any extra bank.

GET/api/assistant/time-saved

Minutes ChalkBot saved this month, and all time.

Price book and settings

An assistant that quotes without the price book will invent prices. Read the catalog, including measurements, costs you and crew pay. Write hours, templates and follow-ups. Tax, PIN policy and card-fee law stay in the app.

GET/api/products

The price book.

GET/api/products/:id

One item with measurements, costs, crew pay, tiers. 404 if missing.

POST/api/products

Add a line the shop actually sells. May include nested measures (measure_type_id, unit_price_cents charge, unit_cost_cents costs you, pay_per_unit_cents crew pay, materials[]) and crew_minimum_cents.

PATCH/api/products/:id

Change a price or a name. Same nested measures if present. Omit measures to leave them.

POST/api/products/bulk

Duplicate or delete. Delete retires (active=0). Duplicate copies name, price, cost, unit, description, measures and tiers, name suffixed " (copy)".

GET/api/measures/types

Names of things you count (sq ft, ln ft).

POST/api/measures/types

Add a unit you count, like sq ft.

GET/api/measures/products

Every measured item's rates.

GET/api/measures/products/:productId

Rates, materials and crew floor for one item.

PUT/api/measures/products/:productId

Replace rates, materials, crew floor.

GET/api/product-tiers?product_id=

Good / better / best prices for one item.

PUT/api/product-tiers/:productId

Replace those tiers.

GET/api/quote-templates

Reuse "deck refresh" instead of rebuilding lines.

GET/api/job-types

The kinds of work this shop does.

GET/api/client-types

Residential, commercial, and the rest of the labels.

GET/api/pipeline-stages

Funnel columns. Do not make these up.

GET/api/admin/prefs

Hours, quiet hours, templates, deposits, follow-ups.

PUT/api/admin/settings

Change hours, templates and visit length. Allowlisted keys only.

When something happens, we tell you

These fire today from Settings, Connectors: a Zapier URL or your own webhook. They are not waiting on developer keys. The names are the ones the funnel actually sends.

lead_created

A new lead arrives, including one from the website form or ingest.

assessment_booked

An estimate visit is booked.

quote_created

A quote is started.

quote_sent

The quote is sent.

quote_viewed

The customer opens the quote.

changes_requested

The customer asks for changes.

job_created

The work is booked in.

What stays in the app

A leaked key should not hire someone, dump the books, or move money with nobody watching. Those doors stay on a signed-in screen.

People and the login

Invites, permissions, PIN resets, trusted devices and closing the account. The API does not hire, lock out, or take over a shop iPad.

Money that needs a person

Charging a card on file, refunds, writing off bad debt, paying the crew. Recording a check is fine. Moving a card is not.

Wiping the books

Merge customers, bulk wipe, and the full export. You can archive an invoice, or delete an unpaid one with no payments. Paid invoices archive. They do not delete.

Wiring the shop

Stripe Connect, the text number, the email domain, and paying Chalk. Plug those in once, in Settings, with your own eyes on it.

FAQ

Can I call this today?

Yes, if you are on the AI plan ($99 a month) and minted a key in Settings, ChalkBot. Core ($49) shops use the browser. The website estimator ingest key is a different thing, and it stays on every plan.

Is this a second API for the AI?

No. The assistant and your own software hit the same commands. Same shop, same rules, same quiet hours. The difference is the key, not a private back door.

What about Core plan shops?

Core ($49) already has the whole toolkit in the browser. It cannot mint or use developer keys. ChalkBot and those keys sit on the AI plan ($99). Website lead forms keep working on Core. There is no Core one-off developer key.

Will a send actually text someone?

Yes, if the shop has a number or email wired, and it is not quiet hours. If neither channel is ready, the call tells you that instead of failing silently.

The board is already live.

Mint a key in Settings → ChalkBot if you are on the AI plan. If you are building against this, say hello.