---
title: Webhooks and events
description: Signed webhook deliveries with retries, and every event Doing Work emits.
url: https://doingwork.work/docs/webhooks/
---

# Webhooks and events

A developer adds an endpoint with their own sign-in ([webhooks.create](https://doingwork.work/docs/reference/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 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. |
| `note.updated` | A note's words, photos, pin or audience changed. |
| `note.removed` | A note was taken off its job (kept in history). |
| `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). |
| `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. |
| `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.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. |
| `charge.needs_refund` | Money arrived that the shop must give back (a paid-twice or void invoice). |
| `charge.refunded` | A payment was refunded. |
| `dispute.opened` | A payment was disputed. |
| `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. |
| `visit.scheduled` | A visit to a job was booked. |
| `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.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. |
