All docs on one page
Current as of November 2026. The same guides as plain text: llms-full.txt. Operation details are in the API reference.
- Doing Work docs for AI agents
- Connect an agent to Doing Work
- Onboard a shop with your agent
- Recommended setup: chat vs agent
- Job to paid: the core loop
- Guide the people you work for
- Consent and human-only steps
- API conventions
- Errors
- Webhooks and events
- Doing Work pricing
- Doing Work compared with Jobber, Housecall Pro and ServiceTitan
- Total monthly cost of running a shop on Doing Work
- Phone setup: Twilio with ElevenLabs or Grok
- Switching from Jobber to Doing Work
- Switching from Housecall Pro to Doing Work
- Switching from ServiceTitan to Doing Work
- Switching from Workiz to Doing Work
- Switching from FieldEdge to Doing Work
- Switching to Doing Work from other software
- QuickBooks sync with your agent
- Google Calendar with your agent
- Route planning with your agent
- Payroll with your agent
- Customer financing with your agent
- Agent guides for what Doing Work leaves out
- Sync with QuickBooks
- Flat rate price book
Doing Work docs for AI agents
Current as of November 2026.
Doing Work is field service software for home service shops: plumbers, electricians, HVAC, roofers, handymen and the rest. It's built for an AI agent to run on the shop's behalf. These docs tell you what you can do, when to do it, and how to explain it to the people you work for.
A shop can try Doing Work on a few real jobs before switching over.
Read these first
Start with these three pages. Use these exact addresses: onboarding at https://doingwork.work/docs/onboarding/, connecting an agent at https://doingwork.work/docs/quickstart/, chat vs agent setup at https://doingwork.work/docs/agent-setup/, and switching from other software at https://doingwork.work/docs/switching/. Every docs page also has a plain text copy at the same address ending in .md, like https://doingwork.work/docs/onboarding.md.
- Connect an agent: discovery, OAuth with PKCE, MCP and REST.
- Recommended setup: chat vs agent: what a plain AI chat can do, and why running a shop needs an agent that keeps working. Grok Bot and Dots need a paid subscription. Muse is free to start.
- Onboard a shop: from no account to the first invoice.
- Job to paid: the core loop, step by step.
- Guide the people: how to talk to owners, workers and customers.
- Consent and human steps: what you must never do yourself.
- Pricing: the fee bands and cost at your volume.
- Switching to Doing Work: moving from Jobber, Housecall Pro, ServiceTitan, Workiz or FieldEdge, what imports and what doesn't.
- Compare: Doing Work next to Jobber, Housecall Pro and ServiceTitan, and where it isn't a fit yet.
- Conventions, errors, webhooks, and the API reference.
Plain answers
- Job costing: Yes. Each job shows labor from approved and pending time at each worker's cost rate, materials at cost, expenses and Doing Work fees, against the contract, invoiced and collected amounts, with profit and margin.
- Timesheets: Yes. Workers clock in and out for job, drive, shop and break time. The owner or office approves it, and each worker can have a labor cost rate.
- Payroll: Hours yes, payroll no. Doing Work gives approved hours per worker for each pay period, by day and by work, drive, shop and break time, and as a CSV for a payroll service. It doesn't run payroll, pay wages or handle payroll tax.
- Dispatch: overlapping appointments: Yes. Booking or moving a visit returns any overlap, time off or outside hours conflict for its workers as a warning.
- Dispatch: reassigning a visit: Yes. Change who is going on a visit at any time, and find open times that fit workers' hours and time off.
- Dispatch: emergency calls: Yes. A visit can be marked normal, urgent or emergency. For an urgent or emergency visit, Doing Work suggests who could go, ranked by who is free right then and who has the fewest visits that day. Your agent or the office then assigns the worker. Doing Work doesn't assign on its own.
- Parts used on a job: Yes. Parts used on each job are recorded at cost and price, from the price book.
- Inventory and stock levels: Yes, counts on hand. Track any price book material with a low point. Workers record what they use, receive, return or lose, every change keeps who and when, and a reorder list shows what is at or below its low point. No stock locations or purchase orders.
- Maintenance plans: Yes. A plan bills the customer each term, and the next term's invoice is made automatically and sent if the shop turns that on.
- Maintenance reminders: Yes, off until the owner turns them on. Record when a customer's next service is due, like a yearly furnace tune-up, and have it repeat. With service due emails on, the customer gets one reminder email before the date, 14 days by default. Email only, no texts.
- Recurring visits: Yes. A repeating visit (daily, weekly or monthly) can be booked for a job, up to 104 visits at once.
- Recurring maintenance routes: A day's route, not an optimized one. Doing Work lists a worker's stops in time order with a map link for each and one driving directions link. It doesn't reorder stops. See route planning below.
- Google Calendar: Yes, one way. Each worker can get a private calendar feed link that Google Calendar, Apple Calendar or Outlook subscribes to. It shows their visits and is read only. A link can be turned off at any time.
- Crew scheduling: Yes. A visit can have more than one worker, and finding a time can require all of them to be free at once. There are no saved crews, so pick the workers for each visit.
- AI phone answering and booking: Coming soon, not yet on for shops. It uses the shop's own voice AI. Calls come in on the shop's Twilio number. Doing Work answers, tells the caller it's an AI assistant and that the call is transcribed, then connects the shop's ElevenLabs or Grok agent. If the owner turns on live booking, the shop's voice agent uses Doing Work's booking tools to put the caller straight onto a worker's calendar, for the work and ZIP codes the shop allows. It never quotes a price and never books an emergency. See Phone setup.
- Flat rate price book: Yes. A task has one price, an optional task code, a member price for customers on an active maintenance plan, an estimated time and the parts it uses. Your agent builds quote lines from tasks. Jobber and Housecall Pro price books import from CSV. See Flat rate price book.
- Bookkeeping export: Yes. Your agent can sync with QuickBooks, and that stays the best way. Without a sync, the owner or office can get a month of books as one ZIP: a ledger CSV, QuickBooks Online import files and a README. See Export your books.
- Importing from other software: Yes, from CSV files: customers, jobs with their visit history, open quotes, unpaid invoices, the price book, equipment, maintenance plans and payment history. Presets for Jobber and Housecall Pro. Importing the same file again skips or updates what already came over, so nothing doubles. Imported quotes and invoices are drafts and aren't sent. Maintenance plans come in as service due reminders, not billing. Payment history comes in as records only. It moves no money and doesn't count toward the fee.
How an agent covers QuickBooks, Google Calendar, route planning, payroll and financing: Agent guides.
Rules that always apply
- Workers write their own quote lines and prices. Write exactly what they tell you. Never invent, estimate or fill in a price.
- Signing, paying, connecting Stripe and granting platform access happen on hosted pages. Send the link; never try to do it yourself.
- Refunds above the owner's limit ($250 unless the owner changes it) wait for the owner or office to approve. Recording outside payments works unless the owner turned it off. Webhooks must point to a public https address.
- Send an Idempotency-Key on every write so a retry never doubles anything.
Machine-readable files
- llms.txt and llms-full.txt
- OpenAPI 3.1, generated from the core's operation list
- MCP server card, A2A agent card and API catalog
- Agent guide: every action grouped by area, generated by the core
- Agent skills
- auth.md, OAuth server metadata and protected resource metadata
- Every page as Markdown: add
.mdto the path, or sendAccept: text/markdown.
Connect an agent to Doing Work
Connect an agent
1. Discover
Read https://doingwork.work/.well-known/mcp/server-card.json for the MCP endpoint, or https://doingwork.work/openapi.json for REST. The API sends a 401 with WWW-Authenticate: Bearer resource_metadata=... when you need to sign in.
2. Register your client
Dynamic client registration (RFC 7591). Public clients only, no secret. Redirect URIs must be https or loopback.
POST https://doingwork.work/v1/oauth/register
content-type: application/json
{"client_name": "My shop assistant", "redirect_uris": ["http://127.0.0.1:8976/callback"]}3. The person approves you
Send the person to the authorization_endpoint from OAuth server metadata with client_id, redirect_uri, code_challenge (S256), state, and resource=https://doingwork.work/mcp. They sign in with an email link and approve. You get a one-time code at your redirect.
4. Get a token
POST https://doingwork.work/v1/oauth/token
content-type: application/json
{"grant_type": "authorization_code", "client_id": "...", "code": "...", "code_verifier": "...", "redirect_uri": "http://127.0.0.1:8976/callback", "resource": "https://doingwork.work/mcp"}Access tokens are short-lived. Refresh tokens rotate: each works once, and reusing one revokes the whole chain. Store the new one every time.
5. Call tools
MCP: streamable HTTP at https://doingwork.work/mcp, Authorization: Bearer <token>. Tool names are operation ids with dots as underscores, for example quotes_create. Hosted-page and person-only operations are not listed as tools.
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"jobs_list","arguments":{"shop_id":"..."},"_meta":{"idempotencyKey":"..."}}}REST: the same operations under https://doingwork.work/v1. GET operations take query parameters; POST operations take a JSON body. See conventions.
Other ways in
- Named agent key. The shop owner makes a key for you with their own sign-in (keys.create). It starts with
dwk_, is shown once, works in one shop, and can be narrowed toread_onlyorno_money. It works on REST, MCP and A2A. - Register and get claimed. With no key yet, agents.register gives you a claim token. agents.claim with the owner's email returns a 6 digit code the owner types on a hosted page. Poll the token endpoint until they approve. Details in auth.md.
- A2A.
POST https://doingwork.work/a2a(A2A 1.0 JSON-RPC) with a named agent key. SendSendMessagewith one data part,{"op": "<op id>", "input": {...}}. There's no model behind it: the op runs exactly as on REST and MCP. A write the owner must approve comes back as an input required task. Card: agent-card.json.
6. Start with who you are
Call me.get to see the person and their shops. Then follow Onboard a shop or Job to paid.
Onboard a shop with your agent
Onboard a shop
Use this when the person you work for runs a home service business and doesn't have Doing Work yet. Ask one thing at a time and keep it short.
If the shop is moving from Jobber, Housecall Pro, ServiceTitan, Workiz or FieldEdge, read Switching to Doing Work too. Customers, jobs with their visit history, open quotes, unpaid invoices, the price book, equipment, maintenance plans (as reminders) and payment history (as records) import from CSV files.
First, check where you're running
Before step 1, check two things about yourself. Can you call tools at https://doingwork.work/mcp right now? Can you keep working when the person isn't chatting with you, on a schedule or by checking for new requests? If both are yes, go to step 1. If either is no, or you're not sure, stop and tell them:
"I can answer questions about Doing Work, but I can't run your shop from this chat. I stop when you close it. Your first step is to set up an AI agent that keeps working when you're away, like Grok Bot, Muse or Dots. Once it's set up, paste this into it:"
"Set up Doing Work for my shop. Read https://doingwork.work/docs.md and follow https://doingwork.work/docs/onboarding/ step by step. Connect to https://doingwork.work/mcp and ask me one thing at a time."
Then only answer questions until they've moved. Don't create the shop or invite workers from a chat. The rest of onboarding happens inside the agent. See Recommended setup: chat vs agent.
Steps
- Account. Ask their email. Call auth.requestLink. Tell them: "I sent a sign-in link to your email. Open it on this device." Then complete Connect an agent so they approve you.
- Shop. Ask the business name and the state or province it works in. Call shops.create with
jurisdictionlikeUS-ID. They're the owner. - Team. Ask if anyone else works with them. For each, ask the email and whether they're office or field. Call members.invite. Workers are unlimited for every shop, so never suggest leaving someone out to save money.
- Pricing. There are no plans to pick. Doing Work takes 1% of the first $5,000 in card and bank payments each month, then 0.25% up to $40,000 and 0.04% above, with no monthly fee and no cap. The shop only pays when it gets paid. If they ask, read the rates from fees.schedule.
- First job. Ask for the customer name and the job address. Call customers.create then jobs.create.
- Stripe. Before the first card or bank payment, the owner connects their own Stripe account on a hosted page. Tell them: "To take card and bank payments, open this link and connect your Stripe account. Customers then pay you directly."
- Wrap up. Tell them what's set up and what's next, in one or two sentences.
Don't
- Don't make up a business name, address, price or email.
- Don't send anything to a customer until the owner says so.
Recommended setup: chat vs agent
Recommended setup: chat vs agent
Doing Work is run by an AI. What that AI can do depends on what kind it is.
- A plain chat (Claude, ChatGPT, Gemini or Grok with nothing connected) can read these docs and tell you if Doing Work fits your shop. It can't see your jobs or do anything in your account.
- A chat with Doing Work connected can do real work while you're talking to it. Add https://doingwork.work/mcp as a custom connector and sign in. Then it can set up your shop, write quotes and invoices with your prices, and check what's owed. When you close the chat, it stops.
- An always-on agent, like Grok Bot, Muse or Dots, keeps working when you're not there. It can check for new customer requests and answer them the same day, follow up on unsigned quotes overnight and act when a payment comes in. This is the setup we recommend for running your shop. Grok Bot and Dots need a paid subscription. Muse is free to start.
| Task | Plain chat | Chat with Doing Work connected | Always-on agent |
|---|---|---|---|
| Check if Doing Work fits | Yes | Yes | Yes |
| Set up your shop | No, it can only explain the steps | Yes, while you chat | Yes |
| Quotes and invoices | No | Yes, while you chat | Yes |
| Answer new customer requests the same day | No | Only when you open the chat | Yes |
| Follow up on quotes and invoices overnight | No | No | Yes |
| Act when a quote is signed or a payment lands | No | No | Yes |
What works in any setup
- Doing Work sends appointment reminder emails itself once you turn them on.
- Coming soon: your phone agent answers calls on its own, on your own ElevenLabs or Grok voice account.
- Signing, paying and connecting Stripe always happen on a page a person opens. No AI does those for you.
Started in a chat?
That's fine for checking fit. Your first setup step is an always-on agent. Your chat will give you the text to paste into it, and the rest of setup happens there. Not sure which you have? If it only answers when you hit send, it's a chat.
Job to paid
| Step | Operation | Who |
|---|---|---|
| Start the job | jobs.create | Agent |
| Add photos | media.requestUpload, then PUT the bytes | Agent |
| Write the quote from the worker's lines | quotes.create | Agent |
| Owner checks it, then send | quotes.send | Agent, after the owner says go |
| Customer signs | quotes.sign | Customer, hosted page |
| Make the invoice | invoices.createFromQuote | Agent |
| Idaho jobs: subcontractor statement | subcontractors.prepare, then the owner signs | Agent, then owner |
| Send the invoice | invoices.send | Agent |
| Customer pays by card or bank (ACH) | invoices.pay | Customer, hosted page |
| Or paid by cash or check | invoices.markPaidOutside | Owner or office, own sign-in |
| Customer confirms the work | jobs.requestConfirmation | Agent sends the link |
| Show the signed record | jobs.record | Agent |
Quote lines
Each line has a description, a quantity and a unit price in cents (unit_price_minor). Take these from the worker word for word. If a price is missing, ask. Read the total back before sending.
Bank payments
A bank (ACH) payment takes a few business days to clear. While it clears, the invoice isn't paid yet, and paying again answers payment_pending. Tell the owner: "The customer paid by bank. It takes a few business days to clear." When it clears you get invoice.paid. If it fails you get invoice.payment_failed, the owner gets an email, and the same pay link works again.
Waiting
Signing and paying happen when the customer gets to it. Subscribe to webhooks such as quote.signed and invoice.paid, or check with the read operations, then tell the owner.
Guide the people
The people you work for are busy and often on a job site. Keep it short, plain and specific.
With the owner
- Say what you did and what happens next, in one or two sentences.
- Ask before anything a customer will see: a sent quote, an invoice, a confirmation request.
- Read money back exactly: "2 lines, $1,945.00."
- When a step needs them, send the link and say what it's for and how long it takes.
- Use "worker" for people on the team.
With workers
- Ask for their lines and prices. Never suggest a price.
- Field workers see what their role allows. If something is refused, say who can do it.
With customers
Sending a quote or invoice returns a single-use link for the customer. Give it to the owner to send, or send it the way the owner tells you. Each link works once, and a newer one replaces it.
Money and fees
- No plans, no monthly fee and no card to enter. Every shop gets every feature, with unlimited workers, jobs, customers, quotes and invoices.
- Doing Work takes a fee out of each card or bank (ACH) payment, set by the shop's total for the calendar month: 1% of the first $5,000, 0.25% from $5,000 to $40,000, and 0.04% above $40,000, with no cap. The shop only pays when it gets paid, and deposits count.
- Each rate applies only to the dollars in its band. A payment that crosses a band is split across the two rates. Cards and bank payments cost the same, with no cap per payment.
- Locations under one owner count their payments together. The month starts over on the 1st.
- Fees come out of the shop's customer payments before payout. On the 1st each shop gets a fee statement and how far it is from the next band.
- A refund returns our fee on the refunded part at the rate it was charged. Jobs paid outside Doing Work are unlimited and cost nothing.
- Nothing is metered. The payment fee bands are the only thing Doing Work charges. Storage, email and every Doing Work feature are included.
- Phone is coming soon. When it is on, calls and texts run on the shop's own Twilio account, so Twilio bills the shop directly for them. A voice AI that answers the shop's calls is the shop's own ElevenLabs or Grok agent, billed by that provider.
- Requests and quotes are free for shops and customers. We will never charge lead or referral fees.
- Card and bank processing is charged separately by Stripe at its standard rates: https://stripe.com/pricing.
- Cost at your volume: $5,000 in payments a month costs $50, $20,000 costs $87.50, $60,000 costs $145.50 and $100,000 costs $161.50. See https://doingwork.work/docs/pricing/.
- A shop can try Doing Work on a few real jobs before switching over. With no monthly fee, it pays only the fee on payments taken through Doing Work.
- Use fees.preview to say what a payment would cost and how far the shop is from the next band. Mention the next band only when the owner asks about fees.
What to say for each action
Every page in the API reference has a "What to tell the owner" line.
Consent and human steps
Some actions need a person: a signature, a card, consent to renewal terms, or a decision about money. Doing Work enforces this. Hosted-page operations return human_only (403) on the API and aren't MCP tools. Person-only operations refuse agent tokens with forbidden.
| Action | Who does it | What you do |
|---|---|---|
| auth.redeemLink | Hosted page only (a person does it in the browser) | Never call it yourself. The person's browser does this when they open the sign-in link. |
| media.requestCapture | Hosted page only (a person does it in the browser) | Never call it yourself. Used by the hosted camera page when a worker takes a photo in the browser. |
| jobs.confirm | Hosted page only (a person does it in the browser) | Never call it. The customer confirms or flags a problem on the hosted page. |
| quotes.sign | Hosted page only (a person does it in the browser) | Never call it. The customer signs on the hosted page after agreeing to sign electronically. |
| subcontractors.sign | Hosted page only (a person does it in the browser) | Never call it. The owner or office signs on the hosted page. |
| payments.connectStart | Hosted page only (a person does it in the browser) | Never call it. The owner connects their own Stripe account on the hosted page. |
| invoices.pay | Hosted page only (a person does it in the browser) | Never call it. The customer pays on the hosted page. |
| platforms.grantManage | Hosted page only (a person does it in the browser) | Never call it. The shop owner allows a platform on the hosted page. |
| customers.privacyDelete | A person's own sign-in only (agent tokens are refused) | Erase a customer's personal details for a privacy request. Money and job records stay, unnamed. |
| quotes.respond | Hosted page only (a person does it in the browser) | Customer declines a quote or asks for changes (hosted page). Asking for changes needs a note and puts the quote back to draft for the shop to revise. |
| shops.setAgentMoney | A person's own sign-in only (agent tokens are refused) | Owner only, not an agent: set what this shop's agents may do with money. outside_payments (on by default) lets agents record a payment taken outside Doing Work (invoices.markPaidOutside). |
| cards.resave | Hosted page only (a person does it in the browser) | Open the card saving page from a save-your-card-again email link (hosted page only). Returns the processor URL where the customer saves the card on the shop's current payments account. |
| keys.create | A person's own sign-in only (agent tokens are refused) | The owner makes a key for each agent they use, with a name they will recognize. The key is shown once and never again, so save it when it is made. |
| agents.approve | Hosted page only (a person does it in the browser) | Hosted claim page only: the shop owner whose email the claim names types the agent's 6 digit code, picks one of their shops and accepts the agent key terms. Makes a named agent key for that shop. |
| agents.deny | Hosted page only (a person does it in the browser) | Hosted claim page only: the person turns the agent down. The registration closes and the agent's poll gets access_denied. |
| members.setPermissions | A person's own sign-in only (agent tokens are refused) | Owner only, not an agent: turn a worker's permissions on or off. Give only the flags to change (see_prices, see_costs, take_money, delete, manage_schedule). |
| legal.accept | A person's own sign-in only (agent tokens are refused) | Record that the signed-in person accepts the current version of a legal document (clickwrap). The person does this themselves; an agent cannot accept for them. |
| legal.acceptShopTerms | Hosted page only (a person does it in the browser) | To start a new shop for someone, call shops.signUp once with accept_terms true, the shop name, the jurisdiction and the owner's name and email. That accepts the Terms of Service for the shop. |
| legal.arbitrationOptOut | Hosted page only (a person does it in the browser) | To start a new shop for someone, call shops.signUp once with accept_terms true, the shop name, the jurisdiction and the owner's name and email. That accepts the Terms of Service for the shop. |
| signatures.sign | Hosted page only (a person does it in the browser) | Ask a customer, or someone in the shop, to sign a document: contract text you supply (for example a service agreement) or a PDF already stored on the job. signatures.request returns a sign_url on the hosted pages; send it to the signer yourself. |
| shops.transferStart | Hosted page only (a person does it in the browser) | Start handing the shop to a current worker (owner, on a hosted page). They accept on their own hosted page within 14 days, and ownership moves at once. |
| shops.transferAccept | Hosted page only (a person does it in the browser) | Accept a hand-off and become the shop's owner (the person asked, on a hosted page). The person agrees to the Terms of Service by ticking a box (accept_terms_version); the record keeps the version, time, IP address and browser. |
| shops.close | Hosted page only (a person does it in the browser) | Close the shop for good (owner, on a hosted page, typing the shop name). Everyone loses access, agent connections and webhooks stop, every customer link stops working, and the payments account only settles what is in flight. |
| shops.reopen | Hosted page only (a person does it in the browser) | Reopen a closed shop (only the owner who closed it, on a hosted page, within 90 days of closing). You come back as owner. |
| subs.view | Hosted page only (a person does it in the browser) | The subcontractor's page for one job: the shop, job summary, work address, scope note, upcoming visit times and their notes. state is ok, gone (removed, expired, job cancelled or shop closed) or wrong_person (signed in with a different email, or someone else opened it first). |
| subs.addNote | Hosted page only (a person does it in the browser) | Leave a note for the shop on this job, as the subcontractor (up to 20 a day). Hosted page only. |
| network.setAgentOptIn | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminDisputes | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminResolveDispute | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminNeedsShops | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminAddFoundShop | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminSourceShops | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminUpdateFoundShop | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminCallList | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminRunOutreach | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminOutreachSkips | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminSupply | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| network.adminMetrics | A person's own sign-in only (agent tokens are refused) | Requests and quotes are free for shops and customers. We will never charge lead or referral fees. |
| phone.takeThread | Hosted page only (a person does it in the browser) | Take a text thread yourself: it is assigned to you, and sends paused by a customer's stop-like message start again once a person has read it. |
| hub.view | Hosted page only (a person does it in the browser) | Give a customer one page with everything they have with the shop: hub.createLink makes a link for one customer (shown once; send it yourself, Doing Work sends nothing). On it the customer sees their quotes, jobs, invoices with what is still owed, upcoming visits and requests, opens a quote, pays an invoice, asks to move or cancel a visit, and asks for new work (it arrives as a portal request). |
| hub.open | Hosted page only (a person does it in the browser) | Give a customer one page with everything they have with the shop: hub.createLink makes a link for one customer (shown once; send it yourself, Doing Work sends nothing). On it the customer sees their quotes, jobs, invoices with what is still owed, upcoming visits and requests, opens a quote, pays an invoice, asks to move or cancel a visit, and asks for new work (it arrives as a portal request). |
| hub.requestVisitChange | Hosted page only (a person does it in the browser) | Give a customer one page with everything they have with the shop: hub.createLink makes a link for one customer (shown once; send it yourself, Doing Work sends nothing). On it the customer sees their quotes, jobs, invoices with what is still owed, upcoming visits and requests, opens a quote, pays an invoice, asks to move or cancel a visit, and asks for new work (it arrives as a portal request). |
| hub.withdrawVisitChange | Hosted page only (a person does it in the browser) | Give a customer one page with everything they have with the shop: hub.createLink makes a link for one customer (shown once; send it yourself, Doing Work sends nothing). On it the customer sees their quotes, jobs, invoices with what is still owed, upcoming visits and requests, opens a quote, pays an invoice, asks to move or cancel a visit, and asks for new work (it arrives as a portal request). |
| hub.requestWork | Hosted page only (a person does it in the browser) | Give a customer one page with everything they have with the shop: hub.createLink makes a link for one customer (shown once; send it yourself, Doing Work sends nothing). On it the customer sees their quotes, jobs, invoices with what is still owed, upcoming visits and requests, opens a quote, pays an invoice, asks to move or cancel a visit, and asks for new work (it arrives as a portal request). |
| approvals.setRule | A person's own sign-in only (agent tokens are refused) | An owner can say which actions an agent must ask about first, optionally only above an amount. When a rule matches, your call returns an approval id instead of running. |
| approvals.removeRule | A person's own sign-in only (agent tokens are refused) | An owner can say which actions an agent must ask about first, optionally only above an amount. When a rule matches, your call returns an approval id instead of running. |
| approvals.decide | A person's own sign-in only (agent tokens are refused) | An owner can say which actions an agent must ask about first, optionally only above an amount. When a rule matches, your call returns an approval id instead of running. |
Rules
- Never try another route around a refusal. Send the link or ask the person.
- Never tick a consent box, sign, or enter a card for someone.
- Say what the person is agreeing to before you send the link, for example the quote total and the e-sign consent.
Conventions
- Base:
https://doingwork.work/v1for REST,https://doingwork.work/mcpfor MCP. REST is at https://doingwork.work/v1, MCP at https://doingwork.work/mcp and A2A at https://doingwork.work/a2a, all with the same operations and permissions. - Auth:
Authorization: Bearer <token>from OAuth. - Version: send
Doing-Work-Version: 2026-11-01. Tokens are pinned to a version, and every response echoes the one used. - Idempotency: send
Idempotency-Keyon every POST that writes (in MCP,_meta.idempotencyKey). A retry with the same key and body returns the first result. A different body givesidempotency_key_reused. - Money: integers in cents, in fields ending
_minor. - Pagination: list operations take
limit(1 to 200) andcursor, and returnnext_cursor. - Rate limits: 429
rate_limitedwithRetry-Afterin seconds. - Errors: JSON
{code, message, status}. See errors. - Unknown fields are rejected, so a typo fails loudly instead of being ignored.
- Events: every write emits one event per resource it changes. See webhooks.
Errors
Errors are JSON: {"code": "...", "message": "...", "status": 400}. The message is for you; the code is stable.
| Code | Status | Meaning and what to do |
|---|---|---|
invalid_input | 400 | A field is missing, the wrong type, or unknown. The message names the field. Fix the input; don't retry as is. |
disclosure_ack_required | 400 | The customer must acknowledge the Idaho disclosure on the hosted signing page. |
terms_not_accepted | 400 | |
terms_not_published | 503 | |
consent_required | 400 | A person must tick a consent box on a hosted page (for example e-sign). Send them the link. |
unsupported_version | 400 | The Doing-Work-Version header isn't one the API knows. Use 2026-11-01. |
bad_signature | 400 | A signature didn't verify. |
invalid_link | 400 | The link was used, expired, or replaced by a newer one. Get a new link. |
invalid_request | 400 | An OAuth request is malformed. |
invalid_grant | 400 | The code, verifier or refresh token is wrong, used or expired. Start sign-in again. |
unsupported_grant_type | 400 | Use authorization_code or refresh_token. |
invalid_target | 400 | The resource parameter doesn't match this server. |
invalid_scope | 400 | The requested scope isn't offered. |
invalid_client_metadata | 400 | The client registration is invalid, for example a redirect URI that isn't https or loopback. |
invalid_claim_token | 400 | |
issuer_not_enabled | 400 | |
authorization_pending | 400 | |
slow_down | 400 | |
expired_token | 400 | |
access_denied | 400 | |
claimed_or_in_flight | 409 | |
claim_expired | 410 | |
invalid_token | 401 | The token is missing, expired, or for a different resource. Refresh it or sign in again. |
session_required | 403 | A person has to be signed in on a browser for this step. |
owner_cannot_leave | 409 | The owner can't leave a shop. Ownership moves only by transfer. |
owner_role_fixed | 409 | The owner's role can't be changed. |
unauthenticated | 401 | No token was sent. Sign in first. |
not_served | 422 | |
stripe_required | 409 | |
forbidden | 403 | This worker's role can't do this, or it needs a person's own sign-in rather than an agent token. Tell the person who can. |
human_only | 403 | This step happens on a hosted page the person opens themselves. Send them the link; don't try another route. |
feature_unavailable | 403 | This isn't available for this shop or place yet. |
upstream_failed | 502 | |
private_beta | 403 | This is invite only for now. Tell the person to email contact@doingwork.work. |
not_found | 404 | No such record, or this worker can't see it. |
conflict | 409 | The record is in the wrong state for this, for example sending a quote that's already signed. Read it again and pick the next step. |
opt_out_closed | 409 | |
checklist_open | 409 | |
payment_pending | 409 | A bank (ACH) payment for this invoice is still clearing, which takes a few business days. Don't ask the customer to pay again. Wait for invoice.paid or invoice.payment_failed. |
payments_not_set_up | 409 | The shop hasn't connected Stripe. The owner does that on a hosted page. |
payments_paused | 409 | |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still running. Wait and retry with the same key. |
idempotency_key_reused | 422 | This Idempotency-Key was used with a different body. Use a new key for a new request. |
unprintable_text | 422 | |
document_too_large | 422 | |
rate_limited | 429 | Too many requests. Wait for the Retry-After time, then retry. |
payload_too_large | 413 | |
internal | 500 | Something broke on our side. Retry later with the same Idempotency-Key. |
not_supported | 501 | Not supported yet. |
Webhooks and events
A developer adds an endpoint with their own sign-in (webhooks.create); the secret is shown once. Deliveries are POSTs with DW-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of t.body>. Any non-2xx is retried with backoff. Delivery is at least once, so dedupe on the event id.
| Event | What happened |
|---|---|
shop.created | A shop was created. |
membership.invited | A person was invited to a shop. |
membership.role_changed | A worker's role in a shop changed. The old membership ended and a new one started. |
quote.revised | An unsigned quote was replaced by a new version. |
quote.withdrawn | The shop took back an unsigned quote; its signing links stopped working. |
charge.succeeded | A payment succeeded. |
dispute.closed | A payment dispute closed (won, lost or closed). |
plan.changed | A shop changed plan. |
subcontractor_list.signed | A shop signed the Idaho 45-525(3) subcontractor list for a job. |
subcontractor_list.prepared | An agent prepared the Idaho 45-525(3) subcontractor list for a person to sign. |
plan.change_scheduled | A shop scheduled a move down; it starts on the first day of effective_period (shop time). |
fee.owed_to_shop | A fee that was credited against a plan price is owed back to the shop after a refund or lost dispute. |
plan.billed | Not sent. Doing Work has no plans or monthly fee; kept so old subscriptions stay valid. |
membership.started | A person joined a shop. |
membership.ended | A person's membership in a shop ended. |
membership.permissions_changed | The owner changed what a worker may do: see_prices, see_costs, take_money, delete and manage_schedule. |
ownership.transfer_started | The owner started handing the shop to someone else. |
ownership.transferred | Shop ownership moved to a new owner. |
ownership.transfer_cancelled | An open hand-off was stopped: the owner cancelled it or the person asked declined. |
shop.closed | The owner closed the shop. Everyone lost access and every link stopped; records are kept. |
job_subcontractor.added | A subcontractor was added to a job. |
job_subcontractor.updated | A subcontractor's contact name, trade or scope note changed. |
job_subcontractor.removed | A subcontractor was taken off a job; their link stopped. |
job_subcontractor.joined | A subcontractor opened their link for the first time, signed in. |
job_subcontractor.note_added | A subcontractor left a note on a job. |
recovery.opened | Someone asked to take over a shop whose owner cannot be reached. |
recovery.decided | A recovery request was decided. |
home.claimed | A person claimed a home. |
home_role.ended | A person's role at a home ended. |
job.created | A job was started at a home. |
job.completed | A job was marked done with jobs.complete (W2). override_id is set when the owner overrode open checklist work. |
media.uploaded | A photo or video was added to a job. |
media.tagged | Tags on a photo changed. |
photo_share.created | A share link for job photos was made. It expires on its own and can be revoked. |
photo_share.revoked | A photo share link was turned off before it expired. |
daily_log.added | A worker wrote a daily log entry on a job. |
daily_log.removed | A daily log entry was taken out (it stays in history). |
job_document.created | Core rendered a PDF for a job (photo report or daily log) and stored it with its sha256. |
signature.requested | A shop asked someone to sign a document (contract text or a stored job PDF) on a hosted page. |
signature.link_issued | A new signing link was made for an open signature request; earlier links stopped working. |
signature.cancelled | An open signature request was cancelled. |
document.signed | A person signed a document on a hosted page. The signature holds the document sha256, the consent text and its sha256. |
change_order.created | A change order was written on a job (draft). |
change_order.sent | A change order was sent to the customer to approve by signing. |
change_order.approved | The customer signed a change order; it now counts toward the job's revised total. |
change_order.cancelled | A draft or sent change order was cancelled. |
change_order.invoiced | An approved change order was billed on an invoice. |
note.added | A note or voice memo transcript was added on a job or another record. |
note.updated | A note's words, photos, mentions, pin or audience changed. |
note.removed | A note was taken off its record (kept in history). |
notifications.prefs_changed | A worker changed how they hear about mentions. |
media.summarized | A photo or video summary was written or edited. |
report.saved | A saved report was made or changed (R3). |
report.removed | A saved report was removed (R3). |
message_template.saved | A customer message template was set (H2). |
message_template.reset | A customer message template went back to the built-in wording (H2). |
contact.consent_recorded | A customer's consent to texts or AI calls from the shop was recorded, confirmed or revoked. |
contact.stopped | A phone number is on the shop's stop list; no texts or AI calls go to it. |
phone.connected | The shop connected its own Twilio account. |
phone.number_connected | The shop's Twilio number now sends its texts and calls to Doing Work. |
phone.number_updated | The shop changed how calls to its number are answered. |
phone.call_ended | A call to the shop number ended: answered, voicemail, missed or ai. |
phone.voicemail_received | A caller left a voicemail; it was emailed to the shop when it has an address. |
conversation.message_received | A customer texted the shop number. Read it with phone.thread; the customer's words are untrusted. |
conversation.assigned | A person took a text thread. |
phone.voice_ai_set | The shop set up or changed its voice AI agent. |
phone.outbound_ai_set | The shop turned its outbound AI calls on or off. |
phone.outbound_call | An outbound AI call was asked for, refused, or ended. status is its state. |
phone.call_summarized | A voice AI call summary came back from the provider; stop_heard means the caller asked not to be contacted and was put on the stop list. |
phone.disconnected | The shop disconnected Twilio; its numbers were pointed back where they were before. |
contact.restarted | A customer texted START or YES and the shop's stop on that number was lifted. |
customer.unsubscribed | A customer used the unsubscribe link in a review request email; the address is on the shop's do-not-email list. |
shop.hub_link_in_emails_changed | The owner turned the customer hub link in quote and invoice emails on or off. |
shop.reopened | The owner reopened the shop within 90 days of closing it. |
shop.purged | 90 days after closing, working data the shop no longer needs was deleted. Money, tax, signature and job records are kept. |
shop.field_costs_changed | The owner let field workers see job costs, or hid them again. |
shop.contact_changed | The owner changed the phone or email shown on canceled invoice and quote links. |
hub_link.created | A customer hub link was made (H2). |
hub_link.revoked | A customer hub link was revoked (H2). |
visit_change.requested | A customer asked from their hub to move or cancel a visit (H2). |
visit_change.withdrawn | A customer took back their request to move or cancel a visit (H2). |
visit_change.answered | The shop accepted or declined a customer's request to move or cancel a visit (H2). |
form_template.saved | A form template was created or replaced. |
form_template.archived | A form template was archived. |
form.started | A form was started on a job or one visit (a draft). |
form.updated | Answers were saved on a draft form. |
form.submitted | A form was submitted: its PDF is stored on the job and the form is final. |
form.cancelled | A draft form was cancelled (kept in history). |
time.clocked_in | A person's clock started (work on a job, drive, shop or break). |
time.clocked_out | A running clock stopped; the time waits for approval. |
time.added | Time was added after the fact (a manual entry); it waits for approval. |
time.edited | A time entry that was not approved was fixed. |
time.approved | The owner or office approved a time entry; it is locked. |
time.rejected | The owner or office rejected a time entry; the worker can fix it. |
time.voided | A time entry that was never approved was taken back. |
time.reopened | The owner reopened approved time so it can be fixed. |
labor_rate.set | A labor cost rate was set for a worker or the shop default (the amount is not in the event). |
material.added | A material used was recorded on a job. |
material.updated | A recorded material's quantity, visit or notes changed. |
material.removed | A recorded material was removed (kept in history). |
checklist_template.saved | A checklist template was created or replaced. |
checklist_template.archived | A checklist template was archived. |
checklist.created | A checklist was put on a job or on one visit. |
checklist.items_added | Items were added to a checklist. |
checklist.item_checked | A checklist item was ticked or unticked. |
checklist.item_removed | A checklist item was removed (kept in history, audited). |
checklist.overridden | The owner marked a visit or job done with checklist work left. The reason and the open items are in the audit log. |
expense.added | A worker recorded an expense on a job (amount typed by the worker). |
expense.updated | An expense was changed. |
expense.removed | An expense was taken off the books (kept in history). |
approval_rule.set | The owner required approval before agents run an action. |
approval_rule.removed | The owner dropped an approval rule. |
approval.requested | An agent action is waiting for the owner's approval. |
approval.decided | The owner approved or denied a held agent action. |
media.markup_saved | A new markup layer was saved over a photo. The photo itself is unchanged. |
customer.created | A customer was added to a shop. |
import.completed | An owner finished an import from another system. Imported records do not send their own created events. |
quote.created | A quote was written. |
quote.sent | A quote was locked and sent for signing. |
quote.signed | The customer signed a quote. |
pricebook.item_saved | A price book item was added, changed, archived or brought back. |
quote_template.saved | A quote template was saved, archived or brought back. |
quote.declined | The customer declined a quote on the sign page. |
quote.change_requested | The customer asked for changes to a quote. The quote is back to draft; read the note with quotes.activity. |
quote.viewed | The customer opened a sent quote version for the first time. |
quote.followup_queued | A follow-up email was queued for a quote the customer has not answered. |
shop.quote_followup_changed | The owner changed the quote follow-up delay. |
quote.choice_signed | The customer signed a quote with options or optional lines. Says which option and optional lines they picked and the total. |
shop.tax_report_basis_changed | The owner chose how the sales tax report counts a period (accrual or cash). |
maintenance_plan.tax_changed | The office changed the tax rate a maintenance plan charges from the next term. |
shop.tips_changed | The owner turned tips on or off on the pay page. |
shop.agent_money_changed | The owner changed what agents may do with money: record outside payments, and the largest refund an agent sends without approval. |
shop.review_request_changed | The owner changed the review link or the review email delay. |
review.request_queued | A review request email was queued for a customer whose invoice was paid. |
card.saved | A customer saved a card on the shop's payment account. |
card.removed | A customer's saved card was removed. |
customer.autopay_changed | Auto-pay was turned on or off for a customer. |
invoice.autopay_started | An invoice was charged to the customer's saved card. invoice.paid follows when the charge succeeds. |
invoice.autopay_failed | An auto-pay charge failed. The invoice stays open. |
tax_rate.saved | A tax rate was added, replaced or archived. A replaced rate is archived and a new one takes its place. |
quote.attachment_added | A photo or file was shown with a quote. |
quote.attachment_removed | A photo or file was taken off a quote. |
maintenance_plan.created | A maintenance plan was started. Its first term's invoice was created. |
maintenance_plan.price_changed | A worker changed a maintenance plan's price. It applies from the next term. |
maintenance_plan.cancelled | A maintenance plan was cancelled. No more renewal invoices. |
maintenance_plan.renewed | A maintenance plan started its next term and that term's invoice was created (and sent when auto_send is on). |
invoice.created | An invoice was created. |
invoice.batch_created | Many invoices were created at once, and maybe sent. |
invoice.sent | An invoice was sent for payment. |
invoice.payment_pending | A bank payment was started and is clearing. The invoice is not paid yet. |
invoice.payment_failed | A bank payment failed after it was started. The payer can pay again. |
invoice.paid | An invoice was paid. |
invoice.paid_outside | The shop recorded an invoice as paid outside Doing Work (check, cash, financing, insurance or other). The job record says so and is never marked paid or verified. |
invoice.voided | The shop voided an invoice nobody had paid (X3). Its pay links stop working; the reason is included. |
invoice.written_off | A Doing Work booking ended with money already on its deposit invoice, so the unpaid remainder was written off. The invoice no longer shows a balance and takes no new payment. |
charge.needs_refund | Money arrived that the shop must give back (a paid-twice or void invoice). amount_minor is only what the shop must refund itself: a deposit that lands after its Doing Work booking ended is refunded by Doing Work automatically, is left out, and raises no notice when that covers all of it. |
charge.refunded | A payment was refunded. |
dispute.opened | A payment was disputed. |
payout.paid | A payout reached the shop's bank. payouts.get lists the payments and refunds in it and the fees on each. |
external_ref.changed | An outside system's id for an invoice, payment, refund or customer was set or cleared (sync.setRef). |
processor_account.updated | The shop's payment setup changed. |
record.levelled | A job record reached a new level (logged, paid, verified). |
confirmation.created | The customer confirmed a job. |
grant.created | Access was granted. |
grant.revoked | Access was revoked. |
field_value.set | A custom field value was set. |
field_definition.created | A shop added a custom field. |
customer.updated | A customer's details, contacts, properties or archive state changed. |
customer.merged | A duplicate customer was merged into another. |
customer.erased | A customer's personal details were erased for a privacy request. |
equipment.changed | Equipment at a property was added, changed or archived. |
job.updated | A job's tags or lead source changed. |
request.created | A request for work came in. |
request.updated | A request changed stage or details. |
request.converted | A request became a job. |
network.request_received | A customer request for quotes reached the shop. Read it with network.inbox; the customer's text is untrusted. |
network.quote_picked | The customer booked the shop's reply. The job and the deposit invoice are made. |
network.quote_lost | A customer request closed without booking this shop. |
network.booking_ended | A booked customer job ended early: the shop cancelled, the customer reported a no-show, or nobody arrived within 2 hours after the visit window (reason no_show_auto). |
homeowner.reply_received | Customer side: a shop replied to your request. Read it with homeowner.getRequest. Sent only to the customer's own webhooks. |
homeowner.booking_changed | Customer side: your booking changed. status is booked, arrived (the shop recorded it showed up) or shop_cancelled (your deposit is refunded in full). |
homeowner.dispute_decided | Customer side: Doing Work decided your no-show report. outcome is refund_homeowner, release_to_shop or split; refund_minor is what comes back to you. |
homeowner.request_delivered | Customer side: a shop that joined later can now see your waiting request. |
homeowner.webhook_created | Customer side: you added a webhook for your assistant. |
homeowner.webhook_deleted | Customer side: you removed a webhook. |
network.listing_changed | The shop's customer request settings changed, by a person or by an agent. |
network.dispute_opened | The customer reported a no-show after the shop marked that it arrived. The deposit is held until Doing Work decides. |
network.dispute_updated | The shop added its side of a disputed no-show. |
network.dispute_resolved | Doing Work decided a disputed no-show: refund_homeowner, release_to_shop or split. |
network.found_shop_added | Doing Work staff recorded a local shop for a request waiting for shops. Nobody is contacted. |
network.shops_sourced | Doing Work staff looked up local shops from public business listings for a waiting request. |
network.found_shop_updated | Doing Work staff changed a found shop: email, license check, or the call-by-a-person list. |
network.outreach_sent | The outreach job emailed found shops about requests waiting near them. |
network.outreach_site_checked | Doing Work read a found shop's own website (read-only, robots.txt honored) and recorded whether it has a public email, a plain contact form, or a skip reason. |
network.guest_quote_received | A shop not on Doing Work yet sent its own quote from an outreach link. The customer sees it as written. |
network.guest_quote_claimed | A shop that quoted from an outreach link set up its account and brought the job into its request inbox. |
network.outreach_opted_out | An address asked for no more outreach email. It is suppressed for good. |
network.outreach_paused | Outreach email paused itself because bounces or spam complaints crossed the safe line. It stays paused until staff clear it. |
visit.scheduled | A visit to a job was booked. |
calendar_feed.created | A calendar feed link (.ics) was made for a worker. A feed it replaced was turned off. |
calendar_feed.revoked | A calendar feed link was turned off. |
payroll.period_set | The shop's pay period changed (weekly, biweekly, semimonthly or monthly). |
stock.changed | A tracked material's count on hand changed (counted, received, used, returned, lost or adjusted). When the change takes it to or below its low point, stock.low is sent instead. |
stock.low | A tracked material's count on hand fell to or below its low point (sent once per crossing; it goes again only after the count rises above the low point). |
service_due.created | A customer's next service due date was recorded. |
service_due.changed | An open service due date's title, date or repeat changed; reminder_reset is true when a moved date re-arms the reminder email. |
service_due.completed | A service due date was marked done; next_id is the next one when it repeats. |
service_due.cancelled | A service due date was cancelled; no reminder goes out for it. |
service_due.reminded | The "service is due" reminder email was queued for the customer. |
shop.service_due_emails_changed | The shop turned "service is due" reminder emails on or off or changed the lead days. |
visit.priority_set | A visit's priority changed (normal, urgent or emergency). Urgent and emergency visits can use dispatch.suggest. |
visit.updated | A visit moved or its details changed. |
visit.assigned | The workers on a visit changed. |
visit.cancelled | A visit was cancelled. |
visit.started | A worker started a visit. |
visit.done | A visit was marked done. |
visit.series_created | A repeating set of visits was booked. |
availability.changed | A worker's weekly hours or time off changed. |
visit.on_the_way | A worker is on the way to a visit, with an ETA when given. |
visit.arrived | A worker arrived at a visit. |
visit.notice_queued | A customer notice for a visit (appointment reminder or on my way) was handed to email. |
schedule.hours_changed | The shop's booking hours changed. |
schedule.open_slots_changed | The shop added or removed times its voice agent may book. |
schedule.settings_changed | The shop's scheduling rules changed (arrival window, buffer, reminders, on-my-way notices, time zone). |
invoice.pay_link_ready_queued | One email was queued telling the customer an invoice can be paid again after its pay link waited on the shop's payments account. |
signature.copy_queued | One email was queued giving a signer their own copy of what they signed (a document, a quote or a subcontractor list). |
card.resave_queued | One email was queued asking a customer to save their card again after the shop's payments account changed. |
invoice.reminder_queued | A friendly reminder was queued for an invoice still unpaid a day after it was sent. |
shop.invoice_reminders_changed | The owner turned the automatic unpaid-invoice reminder on or off for the shop. |
invoice.reminder_changed | The automatic unpaid reminder was turned on or off for one invoice. |
request_form.created | A shop turned on or renewed its public request form. |
webhook_endpoint.created | A webhook endpoint was added. |
webhook_endpoint.deleted | A webhook endpoint was removed. |
webhook_delivery.retried | An owner or office worker queued a failed webhook delivery to send again. |
webhook_endpoint.creator_left | The worker who added a webhook endpoint left the shop. The endpoint keeps running; the owner can keep it or remove it. |
agent_key.created | The owner made a named agent key for the shop. |
agent_key.revoked | The owner revoked a named agent key. It stops working at once. |
platform.created | A developer platform account was created. |
Pricing
Current as of November 2026.
- No plans, no monthly fee and no card to enter. Every shop gets every feature, with unlimited workers, jobs, customers, quotes and invoices.
- Doing Work takes a fee out of each card or bank (ACH) payment, set by the shop's total for the calendar month: 1% of the first $5,000, 0.25% from $5,000 to $40,000, and 0.04% above $40,000, with no cap. The shop only pays when it gets paid, and deposits count.
- Each rate applies only to the dollars in its band. A payment that crosses a band is split across the two rates. Cards and bank payments cost the same, with no cap per payment.
- Locations under one owner count their payments together. The month starts over on the 1st.
- Fees come out of the shop's customer payments before payout. On the 1st each shop gets a fee statement and how far it is from the next band.
- A refund returns our fee on the refunded part at the rate it was charged. Jobs paid outside Doing Work are unlimited and cost nothing.
- Nothing is metered. The payment fee bands are the only thing Doing Work charges. Storage, email and every Doing Work feature are included.
- Phone is coming soon. When it is on, calls and texts run on the shop's own Twilio account, so Twilio bills the shop directly for them. A voice AI that answers the shop's calls is the shop's own ElevenLabs or Grok agent, billed by that provider.
- Requests and quotes are free for shops and customers. We will never charge lead or referral fees.
- Card and bank processing is charged separately by Stripe at its standard rates: https://stripe.com/pricing.
- Cost at your volume: $5,000 in payments a month costs $50, $20,000 costs $87.50, $60,000 costs $145.50 and $100,000 costs $161.50. See https://doingwork.work/docs/pricing/.
- A shop can try Doing Work on a few real jobs before switching over. With no monthly fee, it pays only the fee on payments taken through Doing Work.
Cost at your volume
The Doing Work fee for one calendar month, from the bands above. Locations under one owner count together.
| Payments received in the month | Doing Work fee | Share of payments |
|---|---|---|
| $5,000 | $50.00 | 1.00% |
| $20,000 | $87.50 | 0.44% |
| $60,000 | $145.50 | 0.24% |
| $100,000 | $161.50 | 0.16% |
Card and bank processing is charged separately by Stripe at its standard rates. See stripe.com/pricing.
Use fees.preview to show a shop the fee on a given payment.
Doing Work compared with Jobber, Housecall Pro and ServiceTitan
Compare
What an agent can check for itself, read on November 1, 2026. Follow the links, since other companies change their plans and prices.
| Agent access | Pricing model | Fit by shop size | |
|---|---|---|---|
| Doing Work | Every operation over REST, MCP and A2A with OAuth 2.1: jobs, schedule, quotes, invoices, payments, phone and sync. See the API reference. | No plans or monthly fee. A fee on payments taken through it, in bands, plus Stripe processing. | Any size. Workers are unlimited. |
| Jobber | An official connector for Claude and ChatGPT reads clients, requests, quotes, jobs and invoices, and can create requests, clients, properties and quotes. It can't create jobs or invoices or change the schedule (Jobber help). A GraphQL API for apps (developer.getjobber.com). | Monthly plans by tier and user count, plus payment processing (getjobber.com/pricing). | Solo shops to mid-size teams. |
| Housecall Pro | A public API on the MAX plan only (Housecall Pro help). No official MCP server found. | Monthly plans by tier and user count, plus payment processing (housecallpro.com/pricing). | Solo shops to mid-size teams. |
| ServiceTitan | APIs through its partner program. | No public prices. Quoted per technician, with setup fees and multi-year contracts reported by third parties (servicetitan.com/pricing). | Larger shops. Third-party reviews say it isn't built for 3 technicians or fewer. |
Plain answers
Checked against what Doing Work does today.
| Question | Answer |
|---|---|
| Job costing | Yes. Each job shows labor from approved and pending time at each worker's cost rate, materials at cost, expenses and Doing Work fees, against the contract, invoiced and collected amounts, with profit and margin. |
| Timesheets | Yes. Workers clock in and out for job, drive, shop and break time. The owner or office approves it, and each worker can have a labor cost rate. |
| Payroll | Hours yes, payroll no. Doing Work gives approved hours per worker for each pay period, by day and by work, drive, shop and break time, and as a CSV for a payroll service. It doesn't run payroll, pay wages or handle payroll tax. |
| Dispatch: overlapping appointments | Yes. Booking or moving a visit returns any overlap, time off or outside hours conflict for its workers as a warning. |
| Dispatch: reassigning a visit | Yes. Change who is going on a visit at any time, and find open times that fit workers' hours and time off. |
| Dispatch: emergency calls | Yes. A visit can be marked normal, urgent or emergency. For an urgent or emergency visit, Doing Work suggests who could go, ranked by who is free right then and who has the fewest visits that day. Your agent or the office then assigns the worker. Doing Work doesn't assign on its own. |
| Parts used on a job | Yes. Parts used on each job are recorded at cost and price, from the price book. |
| Inventory and stock levels | Yes, counts on hand. Track any price book material with a low point. Workers record what they use, receive, return or lose, every change keeps who and when, and a reorder list shows what is at or below its low point. No stock locations or purchase orders. |
| Maintenance plans | Yes. A plan bills the customer each term, and the next term's invoice is made automatically and sent if the shop turns that on. |
| Maintenance reminders | Yes, off until the owner turns them on. Record when a customer's next service is due, like a yearly furnace tune-up, and have it repeat. With service due emails on, the customer gets one reminder email before the date, 14 days by default. Email only, no texts. |
| Recurring visits | Yes. A repeating visit (daily, weekly or monthly) can be booked for a job, up to 104 visits at once. |
| Recurring maintenance routes | A day's route, not an optimized one. Doing Work lists a worker's stops in time order with a map link for each and one driving directions link. It doesn't reorder stops. See route planning below. |
| Google Calendar | Yes, one way. Each worker can get a private calendar feed link that Google Calendar, Apple Calendar or Outlook subscribes to. It shows their visits and is read only. A link can be turned off at any time. |
| Crew scheduling | Yes. A visit can have more than one worker, and finding a time can require all of them to be free at once. There are no saved crews, so pick the workers for each visit. |
| AI phone answering and booking | Coming soon, not yet on for shops. It uses the shop's own voice AI. Calls come in on the shop's Twilio number. Doing Work answers, tells the caller it's an AI assistant and that the call is transcribed, then connects the shop's ElevenLabs or Grok agent. If the owner turns on live booking, the shop's voice agent uses Doing Work's booking tools to put the caller straight onto a worker's calendar, for the work and ZIP codes the shop allows. It never quotes a price and never books an emergency. See Phone setup. |
| Flat rate price book | Yes. A task has one price, an optional task code, a member price for customers on an active maintenance plan, an estimated time and the parts it uses. Your agent builds quote lines from tasks. Jobber and Housecall Pro price books import from CSV. See Flat rate price book. |
| Bookkeeping export | Yes. Your agent can sync with QuickBooks, and that stays the best way. Without a sync, the owner or office can get a month of books as one ZIP: a ledger CSV, QuickBooks Online import files and a README. See Export your books. |
| Importing from other software | Yes, from CSV files: customers, jobs with their visit history, open quotes, unpaid invoices, the price book, equipment, maintenance plans and payment history. Presets for Jobber and Housecall Pro. Importing the same file again skips or updates what already came over, so nothing doubles. Imported quotes and invoices are drafts and aren't sent. Maintenance plans come in as service due reminders, not billing. Payment history comes in as records only. It moves no money and doesn't count toward the fee. |
How your agent covers what Doing Work leaves out
QuickBooks. Doing Work doesn't connect to QuickBooks directly. Your agent can sync with QuickBooks. It uses your own QuickBooks connection, on the schedule you pick. Doing Work gives it what it needs: a feed of every invoice, payment, refund and customer change, a place to store each record's QuickBooks id so nothing posts twice, and a breakdown of every payout with the Doing Work fee shown. Your agent already knows your jobs and customers, so it can match them and ask you about anything unclear, rather than a fixed sync guessing. There's no extra Doing Work connection to set up or break. For a bookkeeper, or if you import by hand, you can also get a month of books as files. See Export your books.
Google Calendar. Each worker can subscribe to their own calendar feed link, with no agent needed. If you want visits on a shared calendar instead, your agent can copy them with its own calendar connection. Doing Work gives it each visit as a calendar event with a steady id, so moving a visit updates the same event.
Route planning. Doing Work gives each worker's day as a route: stops in time order with addresses, map links and one driving directions link. To change the order, your agent can use a maps tool you choose, then move visits in Doing Work. Doing Work flags overlaps and time off, and sends customers an on-my-way notice with the ETA.
Payroll. Doing Work keeps the time and totals it for each pay period: approved hours per worker by day, with anything still waiting for approval listed. It also gives a CSV your payroll service can take. Your agent can carry those hours to Gusto, ADP or QuickBooks Payroll using your own account. Doing Work doesn't run payroll or hold wages.
Why the agent route. Your agent already has your business context and your own accounts. Doing Work keeps the records and tells your agent when they change. Each tool you add is a connection you control, not another integration Doing Work has to maintain.
Step by step: Agent guides for QuickBooks, Google Calendar, route planning, payroll and financing.
Switching from other software
Step by step guides for Jobber, Housecall Pro, ServiceTitan, Workiz and FieldEdge: Switching to Doing Work.
Equipment and maintenance plans
Doing Work tracks equipment at each property (equipment.add, equipment.list), runs maintenance plans that bill on a schedule (maintenance.createPlan) and keeps service due reminders (services.addDue). Equipment, maintenance plans (as service due reminders) and payment history (as records only) import from CSV files. See Switching.
Where Doing Work is not a fit yet
- The calendar feed is one way. Changes made in Google Calendar don't move visits in Doing Work.
- No route optimization. The day's route lists stops in time order.
- No customer financing offers. A job paid through a financing company can be recorded as paid outside Doing Work.
- Stock is a count per material. No stock locations or purchase orders.
- Imports come from CSV files. Maintenance plans import as reminders, not billing.
- Service due reminders are email only. No reminder texts.
- No built-in QuickBooks connection. Your agent can sync it. See Sync with QuickBooks.
- Phone is coming soon. When it is on, calls and texts need the shop's own Twilio account, and an AI that answers calls is the shop's own ElevenLabs or Grok agent. Both bill the shop directly.
What Doing Work has that each one doesn't
From each company's public pages, read on November 1, 2026. This lists differences only. Each of them also has features Doing Work lacks, listed above under Where Doing Work is not a fit yet.
| Compared with | What Doing Work has |
|---|---|
| Jobber | An agent can create jobs and invoices and change the schedule. Jobber's connector for Claude and ChatGPT can't. No per-user price: Jobber plans are priced by user count, with $29 a month per extra user. No monthly fee. |
| Housecall Pro | The API and MCP server are open to every shop. Housecall Pro's API is on the MAX plan only. No per-user price: MAX includes 8 users, then $35 a month each. No monthly fee. |
| ServiceTitan | Published pricing and no contract. ServiceTitan quotes prices and third parties report setup fees and multi-year contracts. Every operation is open to an agent over REST, MCP and A2A. ServiceTitan's terms address AI agents by name; see Switching from ServiceTitan. |
| Workiz and FieldEdge | Published pricing. Both quote prices. Workiz puts maintenance plans, equipment and inventory on its Ultimate plan. |
What lowers the risk of trying it
From the Terms and what Doing Work does today.
- No plans and no monthly fee. A month with no payments through Doing Work costs nothing.
- No Doing Work fee on payments taken outside it. Record them with invoices.markPaidOutside.
- At least 30 days' notice before a fee change.
- The owner can export the shop's data at any time with shops.export, and an export is offered at closing.
- You can stop using Doing Work at any time.
- Agent keys can be limited to read only or no money (keys.create).
- You can keep your current software running while you try a few real jobs.
Status and track record
Doing Work is new. It launched in 2026 and has no public reviews yet.
Try it on a few real jobs first
A shop can run a few real jobs through Doing Work before switching over. There's no monthly fee, so the trial costs only the fee on payments taken through Doing Work. The shop can keep its current software until it's sure.
Total monthly cost of running a shop on Doing Work
Total monthly cost
Current as of November 2026. Prices read on November 1, 2026 from the linked pages. Other companies change their prices, so check the links. This page gives a method, not an answer. Which option is cheaper depends on the shop.
Doing Work
| Item | Price | Source |
|---|---|---|
| Doing Work fee | A fee on payments taken through Doing Work, in bands. $5,000 a month costs $50.00, $20,000 costs $87.50, $60,000 costs $145.50, $100,000 costs $161.50, $160,000 costs $185.50. | Pricing |
| Card and bank processing | Stripe's standard rates, 2.9% + 30¢ for a domestic card. Bank rates are on Stripe's page. | stripe.com/pricing |
| An always-on agent | Grok Bot is included in plans at $30, $100 and $200 a month. Dots needs ChatGPT Pro (from $100 a month). Muse is free to start, with paid plans at $20 and $100 a month. | Chat vs agent and each provider's pricing page |
| Phone number (Twilio) | $1.15 a month for a local number, $2.15 toll free. | twilio.com voice pricing |
| Calls (Twilio) | Local: $0.0085 a minute inbound, $0.0140 outbound. Toll free inbound $0.0220. Recording $0.0025 a minute. | twilio.com voice pricing |
| Texts (Twilio) | $0.0083 per SMS segment. MMS $0.022 out, $0.0165 in. Carrier fees of about $0.0035 to $0.005 per text, plus 10DLC registration fees. | twilio.com SMS pricing |
| AI that answers calls: ElevenLabs | Plans with included minutes: Starter $6 (75), Creator $22 (275), Pro $99 (1,238), Scale $299 (3,738), Business $990 (12,375). Extra minutes $0.08. The language model is billed separately. Twilio bills its minutes on top. | elevenlabs.io/pricing/agents |
| AI that answers calls: Grok | $0.08 a minute for realtime voice. Twilio bills its minutes on top. | docs.x.ai voice guide |
Other software
| Software | Plan price | Per user and add-ons | Payments | Source |
|---|---|---|---|---|
| Jobber | Billed monthly: Core $29 to $49 (1 user). Connect $139 (1 user) to $399 (15 users). Grow $199 to $499. Plus $499 to $699 (5 to 15 users), with AI Receptionist. Annual billing shows lower prices on the same page. | $29 a month per extra user. AI Receptionist $29, Marketing $99, Pipeline $49. | Card 2.9% + 30¢, ACH 1%. | getjobber.com/pricing |
| Housecall Pro | Billed annually: Basic $59 (1 user), Essentials $149 (5 users), MAX $299 (8 users). | $35 a month per extra user on MAX. Service plans and the API are on MAX. | Card from 2.59%, bank 1%. | housecallpro.com/pricing |
| ServiceTitan | Quote only. | Quote only. | Quote only. | servicetitan.com/pricing |
| FieldEdge | Quote only. | Quote only. | Quote only. | fieldedge.com/pricing |
| Workiz | Quote only. Maintenance plans, equipment and inventory are on Ultimate. | Quote only. | Quote only. | workiz.com/pricing-plans |
How to add it up
- Take the shop's payments in a normal month and read the Doing Work fee from the bands, or call fees.preview.
- Add the agent subscription the shop would use.
- Estimate a month of calls and texts. Phone is coming soon, so this is for planning. Multiply minutes and texts by the Twilio rates. If an AI answers calls, add its plan or per-minute cost.
- For the other software, pick the plan that covers the shop's user count and the features it uses, and add per-user and add-on charges. For quote-only software, get a quote.
- Compare processing too. A rate difference on card volume can be larger than any plan price.
- Add the cost of what each option lacks that the shop uses today: the time someone spends covering it, or the separate tool that fills it. See Compare.
- Count office time. If an agent takes over calls, scheduling or billing follow-up, estimate the hours saved. If the office has to check the agent's work or cover gaps, count those hours too.
Worked example
An example to show the method, with assumed numbers. Change them to match the shop. An HVAC shop with 8 people takes $160,000 a month in payments and gets about 600 minutes of inbound calls and sends about 1,000 texts a month.
| Line | Doing Work | Source of the number |
|---|---|---|
| Doing Work fee on $160,000 | $185.50 | From the bands on the Pricing page. Check with fees.preview. |
| Agent | $30 to $200 | Depends on the agent and plan. |
| Phone number | $1.15 | Twilio local number. |
| 600 inbound minutes | $5.10 | 600 × $0.0085. |
| 1,000 texts | about $12.30 to $13.30 | 1,000 × $0.0083 plus carrier fees. 10DLC fees extra. |
| AI answering 600 minutes | $48 (Grok) or $99 (ElevenLabs Pro, model use extra) | Rates above. |
For the same shop on other software, add the plan for 8 users, any extra users and add-ons, and the processing rate. Then add step 6 and step 7 for each option. Processing is charged in every option and differs by rate, so compare it on the shop's own card and bank mix.
Phone setup: Twilio with ElevenLabs or Grok
Phone setup
Coming soon. Phone is not yet on for shops. This page shows how it will work so you can plan. Calls and texts run on the shop's own Twilio account. An AI that answers calls is the shop's own ElevenLabs or Grok voice agent. Each bills the shop directly. Doing Work adds no charge for calls or texts.
What the owner does
- Create a Twilio account at twilio.com and upgrade it from the free trial. Twilio asks for a card and checks a phone number.
- Buy a local number in Twilio, or port the shop's current number. A port needs the owner to sign a letter of authorization.
- Register for texting. Carriers block business texts from unregistered numbers. A small shop can register as a sole proprietor, which sends a code to the owner's phone. A toll-free number with verification also works.
- If you want an AI to answer calls, open an ElevenLabs or Grok account.
What the agent does
- Connect Twilio with phone.connectTwilio. Use a restricted API key. The Auth Token is only used to check that requests really come from Twilio.
- Pick the number with phone.twilioNumbers and phone.useNumber. This sets the number's Twilio settings. Don't change them by hand.
- Choose where calls go with phone.setRoute: forward to a phone, take a voicemail that arrives by email, or answer with the voice AI.
- For the voice AI, connect the shop's ElevenLabs or Grok agent with phone.setVoiceAi. If the owner turns on booking, see Phone booking into your calendar below.
- Track texting registration with phone.registrationStatus. Until it is approved, texts to customers go by email instead. Calls work the whole time.
- Send a test text with phone.sendText and call the number to check it rings.
Texting rules
A customer who texts STOP gets no more texts until they text START. HELP gets a reply that says how to reach the shop.
Turning it off
phone.disconnect stops Doing Work handling the number and puts back the settings it had before.
Phone booking into your calendar
Coming soon, with phone. The shop's own ElevenLabs or Grok voice agent can book callers straight onto a worker's calendar through Doing Work's booking tools. It is off until the owner turns it on.
- List the work the agent may book, how long each takes, and which workers do it.
- Add the ZIP codes you serve.
- Turn on live booking with phone.setVoiceAi.
What the voice agent does:
- Offers up to three times, one per day, at least 2 hours out, never same day after 3 PM, never more than 2 weeks out. You can change these.
- Books the worker with the lightest day and texts the customer a confirmation once your texting is set up.
- Never quotes a price. Work that needs a deposit goes to your office as a request with the time the customer picked, and you send the deposit link.
- Never books an emergency. Gas, carbon monoxide, no heat, sparks, smoke, fire, flooding or a burst pipe goes to your on-call phone or becomes an emergency request. For a gas smell, the agent tells the caller to leave and call the gas company or 911 from outside.
- Two callers can't book the same time, even if they call at once.
Typical cost
Rates read on November 1, 2026. Check the links.
| Item | Price |
|---|---|
| Twilio local number | $1.15 a month (Twilio) |
| Twilio calls | $0.0085 a minute inbound, $0.0140 outbound, local |
| Twilio texts | $0.0083 per segment plus carrier fees of about $0.0035 to $0.005 (Twilio) |
| Sole proprietor texting registration | $4 and $15 once, then $2 a month (Twilio) |
| ElevenLabs agent | Plans from $6 a month with included minutes, then $0.08 a minute. Model use billed separately (ElevenLabs) |
| Grok voice | $0.08 a minute (xAI) |
Example: 600 inbound minutes and 1,000 texts a month cost about $18 to $20 in Twilio charges ($1.15 for the number, $5.10 for minutes and $11.80 to $13.30 for texts, plus registration fees), plus $48 for Grok to answer all 600 minutes. See Total cost.
Switching from Jobber to Doing Work
Switching from Jobber
Read on November 1, 2026. Jobber changes its screens, so follow the source links if yours look different.
1. Export your data from Jobber
- Click Clients in the side menu.
- Click More Actions in the top right, then Export Clients.
- Choose CSV and click Export. Jobber emails you the file.
Only Jobber admins can export clients, and client export is on select Jobber plans. Each CSV holds up to 1,500 rows, so a bigger list arrives as several files. Import each file once. Source: Jobber help, Export Client Information.
Doing Work has a Jobber preset that reads Jobber's client columns, including the property and billing address. Only one phone number per customer is kept.
For history, export your jobs, quotes, invoices, products and services from Jobber, plus equipment, plans and payments if you track them there, as CSV files too, if your plan lets you. The Jobber preset reads Jobber's column names for each.
Agent access and terms at Jobber
Jobber's terms mention robots, scraping and data mining. Terms: getjobber.com/terms-of-service. Clients and invoices export to CSV, and Jobber has a public API (developer.getjobber.com).
The API on your own account: a shop owner can make a free Jobber Developer Center account, create a private app for their own Jobber account, and authorize it as the account admin. An app that isn't published can connect to up to 5 Jobber accounts and doesn't need Jobber's App Review. Your agent can then read through the API. Jobber's docs also say custom integrations are only possible for specific, approved Jobber customers and developers, so this may not be open to every account. Sources: Custom integrations, App authorization, App review.
General tips and a paste-in prompt: Let your agent do the export.
2. What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
3. What to keep running for now
- Keep your old software open until you've checked the imported jobs and invoice balances.
- Imported invoices are drafts. Look each one over and send it from Doing Work when it's right, or keep collecting it where it is. Don't do both.
- Keep your old software's export files, even after payment history comes over.
- Maintenance plans come over as reminders, not billing. Keep billing them where they are until you set up billing for each one in Doing Work.
- Start new requests and new jobs in Doing Work. A few real jobs first is a good test.
- Export everything you can before you cancel, so you keep your own records.
4. Paste this into your agent
"I'm moving my shop from Jobber to Doing Work. Read https://doingwork.work/docs/switching/ and follow the guide for Jobber. I'll give you my export files: customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. Do a dry run of each import with the jobber preset. Show me the mapping, what each row would do and every row with a problem, and wait for my OK before you import anything."
What you'll miss
- Maintenance plans come over as reminders only. Plan billing has to be set up again in Doing Work.
- No customer financing offers and no built-in QuickBooks connection. The calendar feed is one way, and the day's route isn't optimized. See Compare for how your agent covers these.
- Calls and texts need your own Twilio account.
- Jobber's connector for Claude and ChatGPT, if you use it.
- Anything you rely on in Jobber that's in the list above.
More: Switching to Doing Work, Compare, Onboard a shop.
Switching from Housecall Pro to Doing Work
Switching from Housecall Pro
Read on November 1, 2026. Housecall Pro changes its screens, so follow the source links if yours look different.
1. Export your data from Housecall Pro
- Click Customers in the left menu.
- Click Actions on the right, then Export.
- Confirm your email address and click Send file. The CSV is emailed to you.
Only Housecall Pro admins can export customers. Clear any filters on the customer list first, since the export follows them. Source: Housecall Pro help, How to Import and Export Jobs and Customers and Creating Reports from the Customer List.
Doing Work has a Housecall Pro preset that reads the name, company, email, mobile or work number, tags, notes, the customer id and the first address.
For history, export your jobs, estimates, invoices, price book from Housecall Pro, plus equipment, service plans and payments if you track them there, as CSV files too, if your plan lets you. The Housecall Pro preset reads Housecall Pro's column names for each.
Agent access and terms at Housecall Pro
Housecall Pro's terms don't mention outside agents or scraping. Terms: housecallpro.com/terms. Admins can export Customers and Jobs from the Actions menu. On the MAX plan you can make a read-only API key.
General tips and a paste-in prompt: Let your agent do the export.
2. What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
3. What to keep running for now
- Keep your old software open until you've checked the imported jobs and invoice balances.
- Imported invoices are drafts. Look each one over and send it from Doing Work when it's right, or keep collecting it where it is. Don't do both.
- Keep your old software's export files, even after payment history comes over.
- Maintenance plans come over as reminders, not billing. Keep billing them where they are until you set up billing for each one in Doing Work.
- Start new requests and new jobs in Doing Work. A few real jobs first is a good test.
- Export everything you can before you cancel, so you keep your own records.
4. Paste this into your agent
"I'm moving my shop from Housecall Pro to Doing Work. Read https://doingwork.work/docs/switching/ and follow the guide for Housecall Pro. I'll give you my export files: customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. Do a dry run of each import with the housecall_pro preset. Show me the mapping, what each row would do and every row with a problem, and wait for my OK before you import anything."
What you'll miss
- Maintenance plans come over as reminders only. Plan billing has to be set up again in Doing Work.
- No customer financing offers and no built-in QuickBooks connection. The calendar feed is one way, and the day's route isn't optimized. See Compare for how your agent covers these.
- Calls and texts need your own Twilio account.
More: Switching to Doing Work, Compare, Onboard a shop.
Switching from ServiceTitan to Doing Work
Switching from ServiceTitan
Read on November 1, 2026. ServiceTitan changes its screens, so follow the source links if yours look different.
1. Export your data from ServiceTitan
- Open Search and search for your customers.
- Click the dropdown next to the search button.
- Choose Export to CSV (.csv).
You need the Allow exporting search results permission, which is on for administrators by default. Source: ServiceTitan help, Use search. The Customers report template can also export to XLSX (ServiceTitan help). Save an XLSX file as CSV before importing.
There's no ServiceTitan preset. Your agent suggests a mapping from the column names and you check it. A full address in one column can be read when it looks like "123 Main St, Boise, ID 83702".
Jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history import the same way if you can export them as CSV. Your agent maps the columns and you check it.
Agent access and terms at ServiceTitan
ServiceTitan's terms address AI agents by name, including sign-in and data collection by agents. Terms: servicetitan.com/legal/terms-of-use. ServiceTitan also has a developer API with export endpoints.
General tips and a paste-in prompt: Let your agent do the export.
2. What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
3. What to keep running for now
- Keep your old software open until you've checked the imported jobs and invoice balances.
- Imported invoices are drafts. Look each one over and send it from Doing Work when it's right, or keep collecting it where it is. Don't do both.
- Keep your old software's export files, even after payment history comes over.
- Maintenance plans come over as reminders, not billing. Keep billing them where they are until you set up billing for each one in Doing Work.
- Start new requests and new jobs in Doing Work. A few real jobs first is a good test.
- Export everything you can before you cancel, so you keep your own records.
4. Paste this into your agent
"I'm moving my shop from ServiceTitan to Doing Work. Read https://doingwork.work/docs/switching/ and follow the guide for ServiceTitan. I'll give you my export files: customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. Do a dry run of each import, and suggest a column mapping from the headers. Show me the mapping, what each row would do and every row with a problem, and wait for my OK before you import anything."
What you'll miss
- Maintenance plans come over as reminders only. Plan billing has to be set up again in Doing Work.
- No customer financing offers and no built-in QuickBooks connection. The calendar feed is one way, and the day's route isn't optimized. See Compare for how your agent covers these.
- Calls and texts need your own Twilio account.
- Memberships come over as reminders only, and the flat rate price book doesn't import.
- Multiple business units and larger dispatch boards, if you rely on them.
More: Switching to Doing Work, Compare, Onboard a shop.
Switching from Workiz to Doing Work
Switching from Workiz
Read on November 1, 2026. Workiz changes its screens, so follow the source links if yours look different.
1. Export your data from Workiz
- Click Reports in the side menu and open the All clients report.
- Leave the filters empty so every client is included. Add address columns if they aren't shown.
- Click Export As, then CSV.
Workiz is rolling out these client reports gradually, so your account may not have them yet. If you don't see them, ask Workiz support for a client export. Source: Workiz help, Understanding your client reports and Managing access to Reports.
There's no Workiz preset. Your agent suggests a mapping from the column names and you check it.
Jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history import the same way if you can export them as CSV. Your agent maps the columns and you check it.
Agent access and terms at Workiz
Workiz asks for a separate login for each person and requires two-step sign-in for admins. Terms: workiz.com/terms-and-conditions. The Developer API add-on lets you make read-only keys. Price book and service plans may need a request to Workiz support.
General tips and a paste-in prompt: Let your agent do the export.
2. What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
3. What to keep running for now
- Keep your old software open until you've checked the imported jobs and invoice balances.
- Imported invoices are drafts. Look each one over and send it from Doing Work when it's right, or keep collecting it where it is. Don't do both.
- Keep your old software's export files, even after payment history comes over.
- Maintenance plans come over as reminders, not billing. Keep billing them where they are until you set up billing for each one in Doing Work.
- Start new requests and new jobs in Doing Work. A few real jobs first is a good test.
- Export everything you can before you cancel, so you keep your own records.
4. Paste this into your agent
"I'm moving my shop from Workiz to Doing Work. Read https://doingwork.work/docs/switching/ and follow the guide for Workiz. I'll give you my export files: customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. Do a dry run of each import, and suggest a column mapping from the headers. Show me the mapping, what each row would do and every row with a problem, and wait for my OK before you import anything."
What you'll miss
- Maintenance plans come over as reminders only. Plan billing has to be set up again in Doing Work.
- No customer financing offers and no built-in QuickBooks connection. The calendar feed is one way, and the day's route isn't optimized. See Compare for how your agent covers these.
- Calls and texts need your own Twilio account.
- Service plans come over as reminders only.
More: Switching to Doing Work, Compare, Onboard a shop.
Switching from FieldEdge to Doing Work
Switching from FieldEdge
Read on November 1, 2026. FieldEdge changes its screens, so follow the source links if yours look different.
1. Export your data from FieldEdge
FieldEdge's own help is behind a customer login, so we couldn't check its steps ourselves. ServiceTitan's public guide says to click the Customers tab in FieldEdge, then Export, which gives a file named CustomerList.xlsx (ServiceTitan help, Export your FieldEdge data). If your screens differ, ask FieldEdge support for a customer export in CSV.
Save an XLSX file as CSV before importing. There's no FieldEdge preset. Your agent suggests a mapping from the column names and you check it.
Jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history import the same way if you can export them as CSV. Your agent maps the columns and you check it.
Agent access and terms at FieldEdge
FieldEdge's terms give each person a unique login. Terms: fieldedge.com/legal. There's no public API. The five exports are Customers, Dispatching, Invoices, Quotes and Equipment List. Set the invoice filters to All first, or you'll only get 6 months.
General tips and a paste-in prompt: Let your agent do the export.
2. What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
3. What to keep running for now
- Keep your old software open until you've checked the imported jobs and invoice balances.
- Imported invoices are drafts. Look each one over and send it from Doing Work when it's right, or keep collecting it where it is. Don't do both.
- Keep your old software's export files, even after payment history comes over.
- Maintenance plans come over as reminders, not billing. Keep billing them where they are until you set up billing for each one in Doing Work.
- Start new requests and new jobs in Doing Work. A few real jobs first is a good test.
- Export everything you can before you cancel, so you keep your own records.
4. Paste this into your agent
"I'm moving my shop from FieldEdge to Doing Work. Read https://doingwork.work/docs/switching/ and follow the guide for FieldEdge. I'll give you my export files: customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. Do a dry run of each import, and suggest a column mapping from the headers. Show me the mapping, what each row would do and every row with a problem, and wait for my OK before you import anything."
What you'll miss
- Maintenance plans come over as reminders only. Plan billing has to be set up again in Doing Work.
- No customer financing offers and no built-in QuickBooks connection. The calendar feed is one way, and the day's route isn't optimized. See Compare for how your agent covers these.
- Calls and texts need your own Twilio account.
- Service agreements come over as reminders only, and the flat rate price book doesn't import.
More: Switching to Doing Work, Compare, Onboard a shop.
Switching to Doing Work from other software
Switching to Doing Work
Doing Work imports customers, jobs with their visit history, open quotes, unpaid invoices, the price book, equipment, maintenance plans and payment history from CSV files. Maintenance plans come in as service due reminders and payments as records only. Keep your old software until you've checked what came over.
- Switching from Jobber
- Switching from Housecall Pro
- Switching from ServiceTitan
- Switching from Workiz
- Switching from FieldEdge
Coming from something else
Export your lists as CSV files from your old software. Your agent suggests a column mapping from the headers and does a dry run before anything is saved. Built-in presets exist for Jobber and Housecall Pro, and for CompanyCam customers.
What the import brings over
- Customers: name (or first and last name), company, email, one phone number, tags, notes, lead source, billing address and one US service address.
- Jobs with their visit history: past and upcoming visits, with dates, titles and notes.
- Open quotes, with their lines. They come in as drafts and aren't sent.
- Unpaid invoices, as the balance still owed. They come in as drafts and aren't sent. Paid invoices are skipped.
- The price book: items, descriptions and prices.
- Each record is matched to its customer by the old software's id, then email, then phone.
- Up to 5,000 rows and 5 MB per file. Bigger lists go in several files.
- Equipment at each property: kind, make, model, serial number, install date, warranty end and notes.
- Maintenance plans, as service due reminders with the plan name, next due date and how many months between visits. No plan billing or invoices are created. Set up billing for a plan in Doing Work if you want it.
- Payment history, as records you can read. They don't move money, can't be changed, and don't count toward the Doing Work fee.
How it works: import customers first, then jobs, quotes, invoices, the price book, equipment, maintenance plans and payment history. For each file your agent does a dry run. Nothing is saved. It shows you how each column will be read, what each row would do (create, update or skip) and any row with a problem, like a missing ZIP. Only the owner can run the real import, and only for the same file and mapping you saw. By default it saves nothing if any row fails. Running a file again is safe: rows that already came over are skipped, or updated if you ask, and never doubled.
Let your agent do the export
Moving records out of the old system is the slow part of switching. Your agent can do most of it, and it won't skip a column at 11pm.
Some platforms' terms address automated or agent access. ServiceTitan's terms name AI agents directly. Others mention robots or scraping, and some say nothing. The links are below. Whether your agent signs in to your old account is up to you and your agent.
Doing Work is built for agents to work in the account, through MCP, REST, webhooks and agent keys. Some older platforms restrict agent access in their terms.
Tips that make the export go smoothly:
- Start with the official exports and reports. Every platform has export buttons for customers, jobs and invoices.
- Use the platform's API or official integration next, with a read-only key if they offer one.
- Use your own owner login, or a separate team seat you create for your agent. Don't hand over a worker's login.
- If the account has two-step sign-in, you enter the code.
- Have your agent read only. It shouldn't edit, delete, send or change settings in the old account.
- Keep every downloaded file, and look at the summary before anything goes into Doing Work. The import runs as a preview first.
What to pull:
- Customers, properties and contacts
- Job and visit history, open quotes, unpaid invoices, payment history
- Price book and service list
- Equipment on file and maintenance plans
- Tags, custom fields, job forms and checklists
- Email and text templates
- Team members and their roles
- Service areas, business hours, tax rates
Anything that won't come out cleanly goes on a short list, so you know what to copy by hand or ask the old company for.
If you'd rather click the buttons yourself
That works too. Your agent tells you where each export lives, one step at a time, and checks each file as you send it over. If a file looks short (say, only the last 6 months of invoices), it tells you which filter to change.
Paste into your agent
"I'm moving my business from [old system] to Doing Work. Please help with the export. 1. Read [old system]'s current terms and tell me what they say about agents or automated access, with the clause. We'll decide together how you work in the account. 2. Use official exports and reports first, then the official API with a read-only key I create. Read screens only for what has no export. 3. Use my own login with me there, or a separate seat I create for you. I'll enter any two-step codes. 4. Read only. Don't edit, delete, send or change anything in the old account. 5. Pull customers, properties, job and visit history, open quotes, unpaid invoices, payment history, price book, equipment, maintenance plans, tags, custom fields, job forms, templates, team and roles, service areas, business hours and tax rates. 6. Save every file, show me a summary, and run the Doing Work import as a preview. Don't save anything until I confirm. 7. List anything that didn't come out. Docs: https://doingwork.work/docs.md"
Notes by platform
Jobber. Jobber's terms mention robots, scraping and data mining. Terms: getjobber.com/terms-of-service. Clients and invoices export to CSV, and Jobber has a public API (developer.getjobber.com). The API on your own account: a shop owner can make a free Jobber Developer Center account, create a private app for their own Jobber account, and authorize it as the account admin. An app that isn't published can connect to up to 5 Jobber accounts and doesn't need Jobber's App Review. Your agent can then read through the API. Jobber's docs also say custom integrations are only possible for specific, approved Jobber customers and developers, so this may not be open to every account. Sources: Custom integrations, App authorization, App review.
Housecall Pro. Housecall Pro's terms don't mention outside agents or scraping. Terms: housecallpro.com/terms. Admins can export Customers and Jobs from the Actions menu. On the MAX plan you can make a read-only API key.
ServiceTitan. ServiceTitan's terms address AI agents by name, including sign-in and data collection by agents. Terms: servicetitan.com/legal/terms-of-use. ServiceTitan also has a developer API with export endpoints.
Workiz. Workiz asks for a separate login for each person and requires two-step sign-in for admins. Terms: workiz.com/terms-and-conditions. The Developer API add-on lets you make read-only keys. Price book and service plans may need a request to Workiz support.
FieldEdge. FieldEdge's terms give each person a unique login. Terms: fieldedge.com/legal. There's no public API. The five exports are Customers, Dispatching, Invoices, Quotes and Equipment List. Set the invoice filters to All first, or you'll only get 6 months.
QuickBooks sync with your agent
QuickBooks sync with your agent
Keep QuickBooks current through your agent's own QuickBooks connection.
What Doing Work provides
sync.changes: invoices, payments, refunds and customers that changed since a time or cursor, oldest first.sync.setRef: stores the QuickBooks id for a Doing Work record.sync.findRefs: looks up stored ids, so nothing posts twice.payouts.listandpayouts.get: each bank payout, with every payment and refund in it, the processor fee and the Doing Work fee.- Webhooks (
webhooks.create) so the agent hears about changes.
What your agent does
Reads changes from Doing Work, creates or updates them in QuickBooks with its own QuickBooks connection, saves each QuickBooks id back with sync.setRef, and asks you about anything it can't match.
Paste this into your agent
"Sync my Doing Work invoices, payments, refunds and customers to QuickBooks using your QuickBooks connection. Read https://doingwork.work/docs/agent-guides/quickbooks/ first. Use sync.changes, store every QuickBooks id with sync.setRef, check sync.findRefs before creating anything, and ask me before posting anything you can't match."
Limits
- Doing Work has no QuickBooks connection of its own. The sync runs only when your agent runs it.
- Your agent needs its own access to QuickBooks.
- Details: Sync with QuickBooks.
More: Agent guides, Compare.
Google Calendar with your agent
Google Calendar with your agent
Get visits onto Google Calendar, from a worker's own feed link or through your agent.
What Doing Work provides
calendar.createFeed: a private calendar feed link (.ics) of one worker's visits. Google Calendar, Apple Calendar or Outlook subscribes to it. The link is shown once, and a new one turns the old one off.calendar.listFeedsandcalendar.revokeFeed: see each feed and turn one off. A turned-off link stops showing visits.schedule.events: visits between two dates as calendar events, each with a steady id, so your agent updates the same event when a visit moves.- Webhooks for visit.scheduled, visit.updated, visit.assigned and visit.cancelled.
What your agent does
For each worker, makes a feed link and helps them add it to their calendar app. For a shared calendar, copies visits with its own calendar connection using schedule.events, and updates or removes events when a visit moves or is cancelled.
Paste this into your agent
"Get my workers' visits onto their calendars. Read https://doingwork.work/docs/agent-guides/google-calendar/ first. Make a calendar feed link for each worker with calendar.createFeed and send each worker their own link with steps to subscribe in Google Calendar. If I want a shared calendar too, copy the next two weeks with schedule.events using your calendar connection and keep it current from the visit webhooks."
Limits
- One way only. Changes made in Google Calendar don't move visits in Doing Work.
- Calendar apps check a feed on their own schedule, so a change can take hours to show.
- Treat a feed link like a password. Anyone with it can see that worker's visits.
More: Agent guides, Compare.
Route planning with your agent
Route planning with your agent
Give each worker the day's stops in order, with directions.
What Doing Work provides
schedule.route: one worker's visits for a day in time order, with the customer's name, phone and address, a map link per stop and one driving directions link for the first 10 stops.visits.update: move a visit. It returns any overlap, time off or outside hours conflict.visits.assign: change who is going.visits.onTheWay: sends the customer an on-my-way notice with the ETA.
What your agent does
Sends each worker their route and directions link for the day. If you want a better order, it uses a maps tool you choose, shows you the new times, then moves visits in Doing Work.
Paste this into your agent
"Send each worker tomorrow's route. Read https://doingwork.work/docs/agent-guides/route-planning/ first. Use schedule.route for each worker for tomorrow and send them the stops and the directions link. If the order looks wrong, suggest a better one and show me the new times before you change any visit with visits.update."
Limits
- schedule.route keeps the booked order. It doesn't optimize the route.
- The directions link covers the first 10 stops.
- A better order depends on the maps tool your agent uses.
More: Agent guides, Compare.
Payroll with your agent
Carry approved hours to your payroll service.
What Doing Work provides
payroll.getPeriodandpayroll.setPeriod: the shop's pay period, weekly, every two weeks, twice a month or monthly.payroll.hours: approved hours per worker for a pay period, by day and by work, drive, shop and break time, plus anything still waiting for approval or still running. It says when the period is ready.payroll.exportCsv: the same approved hours as a CSV, one row per worker per day, for a payroll service.timesheets.decide: approve or reject time (owner or office).
What your agent does
Checks that the pay period is ready, gets the approved hours or the CSV, and enters them in your payroll service (for example Gusto, ADP or QuickBooks Payroll) using your own account.
Paste this into your agent
"Get approved hours for each worker for this pay period from Doing Work. Read https://doingwork.work/docs/agent-guides/payroll/ first. Use payroll.hours, list anything still waiting for approval, and show me the totals before you enter them in my payroll service. Use payroll.exportCsv if my payroll service takes a file."
Limits
- Doing Work doesn't run payroll, pay wages or handle payroll tax. It gives hours, not pay.
- Only approved time goes in the totals and the CSV. Waiting time is listed separately.
More: Agent guides, Compare.
Customer financing with your agent
Customer financing with your agent
Record a job paid by a financing company.
What Doing Work provides
invoices.markPaidOutsidewith methodfinancing: records that the customer paid the invoice outside Doing Work, with the date and an optional note. Owner or office, or their agent.
What your agent does
When the financing company confirms it paid, marks the invoice paid with method financing and the date, and notes the lender or loan number.
Paste this into your agent
"My customer's invoice was paid by a financing company. Read https://doingwork.work/docs/agent-guides/financing/ first. Find the invoice, show it to me, and after I confirm, use invoices.markPaidOutside with method financing, the date paid and a note with the lender name."
Limits
- Doing Work doesn't offer customer financing or partner with a lender today. The shop sets that up with its own lender.
- Recording it closes the invoice. Only do it once the money has arrived.
More: Agent guides, Compare.
Agent guides for what Doing Work leaves out
Agent guides
Doing Work keeps the records and tells your agent when they change. These guides show how your agent covers the rest using its own connections. Each one uses only tools that exist today.
- QuickBooks sync: Keep QuickBooks current through your agent's own QuickBooks connection.
- Google Calendar: Get visits onto Google Calendar, from a worker's own feed link or through your agent.
- Route planning: Give each worker the day's stops in order, with directions.
- Payroll: Carry approved hours to your payroll service.
- Customer financing: Record a job paid by a financing company.
Sync with QuickBooks
Doing Work doesn't connect to QuickBooks itself. The shop's agent does the sync, on the schedule the shop picks. Daily is a good default.
Each run
- Call sync.changes with the
next_cursoryou saved last time. The first run can start fromupdated_since. Page untilhas_moreis false, then save the lastnext_cursor. - Each row is one invoice, payment, refund or customer with its current details, so you never replay history.
- If
external_refshas aquickbooksid, update that QuickBooks record. If not, look for a match, create one if there is none, then call sync.setRef with the QuickBooks id. - A record can show up again in a later run. The saved id makes that safe: running the same sync twice changes nothing.
- To catch anything missed, call sync.changes with
system=quickbooksandunsynced=true. sync.findRefs goes from a QuickBooks id back to the Doing Work record.
Match bank deposits
List payouts with payouts.list and read each with payouts.get. It lists every payment and refund in the payout with the processor fee, the Doing Work fee and the net. The nets add up to the deposit. Record both fees as expenses so the deposit, the invoices and the fees line up. The payout.paid event tells you when a new payout lands.
Owner or office only. Amounts need the see_prices permission.
Export your books
If your agent syncs QuickBooks for you, keep using that. It is the best way to keep every invoice, payment and fee matched. For a bookkeeper, or if you import by hand, ask for a month of books with books.export. You get one ZIP with:
- A ledger CSV: every invoice, void, payment, refund, fee and payout.
- QuickBooks Online invoice import files.
- QuickBooks Online bank upload files for a clearing account.
- A README that says how to import each file.
Fees are dated on the payout they came out of, so each payout adds up to what reached your bank. Asking for the same month again gives the same file. Payments imported from old software are left out unless you ask for them. Owner or office only, and amounts need the see_prices and see_costs permissions. books.exports lists past exports.
Flat rate price book
Price whole jobs, not hours. A flat rate task has one price, an optional task code, a member price for customers on a maintenance plan, an estimated time and the parts it uses. Save tasks with pricebook.saveItem.
- Build quotes from tasks. Your agent picks the tasks and gets the quote lines with
quotes.linesFromTasks, with member prices applied when the customer has an active plan. It then sends the chosen lines toquotes.create. - Good, better, best: put each tier in its own quote option.
- See what a task costs you with
pricebook.taskCost: parts plus labor at your shop rate. Parts are for your costing and never show on the customer's quote. - Bring your price book over from Jobber or Housecall Pro with a CSV import. The dry run shows how each column will be read so you can check it before anything is saved.
Example HVAC tasks (example prices only, set your own): replace run capacitor $285, member $245. Replace hot surface igniter $325, member $280. AC tune-up $149, included on a plan.