WRAPTORHelp & Docs
The Wraptor handbook

API & Webhooks — connect Wraptor to Zapier and your own tools

Who it's for

Owners (and the tech-comfortable manager) who want Wraptor talking to the rest of their stack: Zapier automations, a custom dashboard, a spreadsheet that pulls live numbers, or lead sources that push straight into the Lead Inbox. Included with Wraptor Shop and Wraptor Fleet.

Where it fits

Outside the job workflow itself — this is the integration layer around it. Events fire from the moments that matter to the shop: a lead arrives, a job moves a stage, a customer approves a quote, a payment lands.

Overview

Two halves:

  • The REST API (https://wraptor.app/api/v1) lets outside software read your customers, jobs, quotes, invoices, leads and purchase orders — and create customers and leads. Every request authenticates with an API key you mint in Settings.
  • Outbound webhooks push events to a URL you control the moment they happen, signed so your receiver can prove they came from Wraptor. This is what a Zapier trigger rides on.

The machine-readable spec lives at /api/v1/openapi.json.

Screens & navigation

Settings → API & Webhooks (under Integrations & Billing, or straight to /settings?section=developer). One panel, two cards:

  • API Keys — every active key on one row: its name, its display prefix (wrk_live_a1b…), either "used 3d ago" or "never used", and a Revoke… link at the end.
  • Webhook Endpoints — every registered URL with the events under it ("All events" when you picked none), a green dot when it is on and a grey one when it is off, and Test · Disable/Enable · Delete…. Underneath, Recent Deliveries lists the last 20 real deliveries: the event name, the attempt number once one has been retried, the error and HTTP code on a failure, and how long ago it ran.

Capabilities

API keys

  • Create key — name it after what will hold it ("Zapier", "reporting sheet"). The full key appears once, in a copy box headed "Your new API key — copy it now, it won't be shown again." Only a hash and the first twelve characters are stored; Wraptor cannot show it again.
  • Revoke… — a two-step inline confirm. It takes effect immediately.
  • Up to 10 active keys per shop. Each key is shop-wide: full access to everything below, with no per-resource scopes.
  • The last-used time is refreshed at most every five minutes, so a busy key can read a few minutes behind.

REST API

  • List and get by id: customers, jobs, quotes, invoices, purchase-orders. Leads are list-only.
  • Write: POST /customers and POST /leads, both answering 201.
  • Lists paginate by cursor: ?limit= (25 by default, 100 at most) and ?cursor= taken from the previous page's next_cursor, which is null on the last page. ?updated_since= (an ISO 8601 timestamp) narrows a list to what changed since your last poll — the Zapier polling filter. Leads never change once captured, so theirs filters on the created time.
  • A successful read returns { "data": … }, a list adds next_cursor, and anything that fails returns { "error": { "code": …, "message": … } } with a matching HTTP status.
  • GET /me verifies a key and names the shop (Zapier's auth test).
  • 120 requests a minute per key; over the limit returns 429 with a Retry-After header.
  • GET /jobs leaves out the seeded sample job.
  • An API-created lead lands in the Lead Inbox and rings the bell ("New API lead: …"). Its source is api, and it files under the Directory filter on Leads → Leads.

Webhook events

job.created, job.stage_changed, quote.approved, quote.declined, proof.approved, proof.changes_requested, document.signed, invoice.paid, invoice.deposit_paid, lead.created, booking.created, customer.created, inspection.completed, purchase_order.ordered, purchase_order.received, call.completed, call.missed, payroll.finalized.

Every delivery uses the same envelope — event, entity_type, entity_id, created_at, and a data object holding that event's own fields:

{
  "event": "job.stage_changed",
  "entity_type": "job",
  "entity_id": "…",
  "created_at": "2026-09-06T15:04:05.000Z",
  "data": { "job_number": "…", "title": "…", "from": "…", "to": "…" }
}
  • job.created fires when a job is created in the app — the New Job form, or Rex on your say-so. A job minted by a quote approval does not repeat it — that arrives as quote.approved, whose payload carries the new job_id — and neither do jobs added by a bulk import.
  • quote.approved and quote.declined fire when the customer answers on their own quote page. Recording the yes yourself with the Mark approved sheet does not fire them.
  • invoice.paid and invoice.deposit_paid fire for card and bank payments through Stripe and for payments you record by hand (cash, Zelle, check). Either way the payload carries invoice_number, amount, method, invoice_total, fully_paid and job_id.
  • inspection.completed fires when a check-in/check-out vehicle inspection is submitted (either lane — installer tablet or front desk). The payload carries the report id, job id, kind, each checklist item's grade, photo and video counts, and the signer name/time — no file URLs.
  • purchase_order.ordered fires when a PO is marked ordered or emailed to the vendor; purchase_order.received fires when it becomes fully received (a short-close counts, flagged short_closed: true). Payloads carry the PO number, status, vendor id/name, the ordered/expected/received timestamps, line count and cent totals — line detail comes from GET /purchase-orders/{id}.
  • call.completed / call.missed fire when a call to the shop's tracking number ends (answered vs. not), so they only ever arrive once call tracking is switched on for your shop. Payloads carry the caller and tracking numbers, the matched customer/lead ids (null if unrecognized), and — for completed calls — the duration in seconds. See calls.md.
  • payroll.finalized fires when a pay period is finalized on Money → Payroll. The payload carries the period start/end, line count, total hours, and base/commission/total cent amounts — deliberately no per-person lines (wages stay inside the app; export the CSV or PDF for detail).

Endpoints

  • Add endpoint — paste the URL and tap the events you want. Pick none and the endpoint takes all of them ("No events selected — the endpoint receives all events."). The signing secret (whsec_…) is shown once, right after you add it. Up to 10 endpoints per shop.
  • Verify — each POST carries wraptor-id, wraptor-timestamp, and wraptor-signature: v1,<base64 HMAC-SHA256> over id.timestamp.body, keyed with your secret's base64 part (everything after whsec_) — the svix scheme, so any svix client library verifies it by mapping the header names. The user agent is Wraptor-Webhooks/1.0.
  • Retries — a failed delivery is due again after 2 minutes, then 8, then every 15, for up to 6 attempts (about 55 minutes end to end). The retry sweep runs every half hour, so a re-send can land later than its due time. An endpoint whose last 3 deliveries all ran out of attempts is switched off and the row reads "auto-disabled after repeated failures — fix the URL and re-enable".
  • Test — fires a test.ping at one endpoint and answers with a Test delivered (HTTP 200) toast, or Test failed and the reason. Test pings stay out of Recent Deliveries, and the button is greyed out while an endpoint is disabled.
  • Disable / Enable / Delete… — disabling stops deliveries and keeps the endpoint and its secret listed; re-enabling clears the auto-disable stamp; deleting removes it for good.
  • Manage endpoints from the API too: GET and POST /api/v1/webhooks, then GET, PATCH and DELETE /api/v1/webhooks/{id}. Create returns the secret once; delete unsubscribes. That is the REST-hook shape Zapier expects.

Step-by-step tasks

  1. Connect a spreadsheet or dashboard

    1. Settings → API & Webhooks → type a name → Create key.
    2. Copy the key from the one-time box, then click I've saved it.
    3. Call GET https://wraptor.app/api/v1/jobs with header Authorization: Bearer wrk_live_….
    4. Follow next_cursor until it is null; poll with updated_since after.
  2. Get notified the moment a lead arrives

    1. Add endpoint with your receiver URL, subscribed to lead.created.
    2. Store the one-time secret; verify wraptor-signature on each POST.
    3. Click Test to confirm your receiver answers 2xx.
  3. Push leads in from an outside form

    1. POST /api/v1/leads with { "name": "...", "contact": "...", "vehicle": "...", "service": "...", "timeline": "...", "message": "..." } — only name and contact are required.
    2. The lead appears under Leads → Leads and rings the bell.

Settings & permissions

  • API keys and webhooks come with Wraptor Shop and Wraptor Fleet (and the grandfathered Wraptor Pro (original)). On Wraptor Starter the panel reads "API access is included with Wraptor Shop." with an Upgrade to Wraptor Shop → link instead of the two cards.
  • The 7-day card-backed trial does not open it — API access needs a live paid subscription. A failed payment keeps working through the grace period; once the subscription actually ends, existing keys stop authenticating (403, code plan_required) while your endpoints stay registered.
  • Opening Settings needs the settings permission, and this panel itself runs on the billing permission — reading the key list, creating a key and managing endpoints all require it. Owners and Managers have both.

Tips & common pitfalls

  • Copy secrets when shown. Keys and signing secrets appear exactly once. Lose one → revoke/delete and create a fresh one; nothing else re-reveals it.
  • Answer webhooks fast with a 2xx. Do your real work after responding — a slow receiver hits the 10s timeout and looks like a failure.
  • Don't redirect. Wraptor never follows a 3xx, so a redirect counts as a failed delivery. Register the final URL.
  • Use wraptor-id for idempotency. Retries reuse the same id; process each id once.
  • Localhost and private-network URLs are rejected — receivers must be publicly reachable, over http or https.
  • Deleting an endpoint stops its deliveries immediately; disabling keeps it listed so you can re-enable without re-sharing a secret.
  • Marketing — the Lead Inbox, where API-created leads land.
  • Jobs — the stages behind job.stage_changed.
  • Invoicing & Billing — the payments behind invoice.paid.
  • Plans — what each tier includes.