Skip to main content

API keys & the public API (with Zapier & Make recipes)

Mint a key, call the /api/v1 REST surface, subscribe to webhooks, and wire Service VIN into Zapier or Make — no code required for the common flows.

Settings & integrations10 in this section

Settings → API keys is the door to Service VIN's public REST API — the same credential Zapier, Make, and your own scripts ask for. A key lets an outside tool read your leads, customers, quotes, invoices and jobs, and — if you give it full access — create leads and customers and subscribe to live webhooks, all scoped to this one shop. Only owners and managers (the shop.manage permission) can mint or revoke keys; everyone else sees the page read-only.

Every key is minted with a scope, and it's the single most useful safety setting on the page. Read-only can GET every resource and change nothing — the right choice for a reporting script, a dashboard, or anything that only looks. Full access can also create leads and customers and manage webhook subscriptions, which is what Zapier and Make need. A read-only key that tries to write is refused with a 403 and the code insufficient_scope; nothing is created. On top of that, mint one key per tool (name it "Zapier" or "Reporting script") so you can revoke one without disturbing the others.

A key can also be given an expiry date — handy for a contractor, an agency, or a one-off migration script. It works through the whole of that day (your shop's timezone) and then simply stops; expired keys show greyed in the list with an Expired badge, and calls with one get a 401 saying so. Leave the field blank and the key never expires, which is what every key created before scopes shipped does.

Create a key

  1. 1

    Name it after what will use it

    In the Create a key box, type a name like Zapier or Nightly export. The name is pure labelling — it just helps you tell keys apart later.

  2. 2

    Choose what it can do

    Pick Read-only (recommended for anything that just reports) or Full access (needed for Zapier, Make, or any tool that creates records). Optionally set an expiry date — leave it blank and the key lasts until you revoke it. Neither can be changed afterwards: to widen or narrow a key, revoke it and mint a replacement. Then click Create key.

  3. 3

    Copy the key immediately

    The full key (svk_live_…) appears exactly once, in a highlighted card at the top of the page, with a copy button. Only a one-way hash is stored — Service VIN itself cannot show it to you again.

  4. 4

    Paste it into your tool

    Give it to Zapier / Make / your script now. If you navigate away before copying, the key is gone for good — just revoke it and mint a fresh one.

The key shows once — then never again

After minting, the page only ever shows the first few characters (the prefix, e.g. svk_live_a1b2…) so you can recognise which key is which. There is no "reveal" button and no recovery. Lost a key? Revoke it and create a new one — it's a 10-second job.

Each row in the key list shows its name, a Read-only or Full access badge, its prefix, when it was created, when it was last used (updated at most once a minute, so it stays honest without a write per request), and its expiry if it has one. Click Revoke to kill a key instantly: anything still using it starts getting a 401 immediately. Revoked and expired keys stay in the list, greyed with a Revoked or Expired badge, as an audit trail — they can't be un-revoked or extended, so mint a new one to rotate.

Base URL, auth & the core endpoints

CallWhat it does
GET /api/v1/meReturns the shop behind the key, plus the key's own scope and expiry — the quickest credential test.
GET /api/v1/leadsList leads, newest first. POST creates one (fires the same pipeline as your website form).
GET /api/v1/customersList or filter customers by email= / phone=. POST creates one.
GET /api/v1/quotesList quotes; filter with status=draft|sent|viewed|accepted|declined|expired.
GET /api/v1/invoicesList invoices with a live balance; filter with status=open|partial|paid|….
GET /api/v1/jobsList jobs; filter with stage=. Job creation returns 501 on purpose — create a lead and convert it in the app.

Everything lives under /api/v1 and is authenticated with a single header. Send your key as a Bearer token on every request:

The request shape

curl https://app.yourshop.com/api/v1/leads \ -H "Authorization: Bearer svk_live_…" Success is always an envelope — { "data": …, "meta": { "next_cursor": "…" } }. Errors are always { "error": { "code": "…", "message": "…" } }. Lists page by keyset cursor: pass limit (default 25, max 100) and the previous page's meta.next_cursor as cursor (treat it as opaque — it is not a timestamp); next_cursor is null on the last page. Every endpoint, field and error code is in the API reference, which also serves a machine-readable OpenAPI 3.1 spec you can import into Postman or Insomnia.

Rate limit: 120 requests/minute per key

Go over and you get a 429 with code rate_limited. Back off a few seconds and retry. If you're syncing a lot, page with the cursor rather than hammering — and spread bulk jobs out.

Webhooks (REST hooks) are how you get *pushed* events instead of polling. Subscribe a URL to any of lead.created, quote.accepted, job.stage_changed, invoice.paid, or message.received, and Service VIN POSTs a signed JSON envelope to that URL whenever the event fires. POST /api/v1/hooks with { "url": "…", "events": ["lead.created"] } subscribes (omit events to get everything); the response returns a whsec_… signing secret exactly once — store it. GET /api/v1/hooks lists your endpoints (no secrets), and DELETE /api/v1/hooks/{id} unsubscribes. Verify each delivery with the X-ServiceVIN-Signature header (HMAC-SHA256 over t + "." + raw_body) and reject anything older than ~5 minutes — the exact recipe, with runnable code, is in the API reference.

Recipe — push new leads into a Google Sheet with Zapier

  1. 1

    New Zap → Webhooks by Zapier → Catch Hook

    Zapier gives you a https://hooks.zapier.com/… catch URL. Copy it.

  2. 2

    Subscribe that URL to lead.created

    Call POST /api/v1/hooks once (from a terminal, Postman, or a one-off Zapier "POST" step) with body { "url": "<your catch URL>", "events": ["lead.created"] } and your Authorization: Bearer header. You'll get back the subscription and its secret.

  3. 3

    Fire a test lead

    Create a lead in the dashboard (or POST /api/v1/leads). Zapier catches the envelope; map data.name, data.email, data.phone to your action.

  4. 4

    Add a Create Spreadsheet Row action

    Point it at your sheet, map the fields, turn the Zap on. Every new lead now lands in the sheet within seconds — no polling, no API key stored in Zapier at all for the incoming direction.

Recipe — create a lead from a Facebook/webform in Make

  1. 1

    Start a Make scenario with your form trigger

    e.g. Facebook Lead Ads, Typeform, or a Make custom webhook that your site posts to.

  2. 2

    Add an HTTP → Make a request module

    Method POST, URL https://app.yourshop.com/api/v1/leads, a header Authorization: Bearer svk_live_…, and a JSON body mapping the form fields: { "name": "{{name}}", "email": "{{email}}", "phone": "{{phone}}", "source": "facebook", "notes": "{{message}}" }.

  3. 3

    Run it once to confirm a 201

    A 201 with the created lead means it worked — and because it fires the same lead.created pipeline as your website form, your Service VIN automations (and any webhooks) run on it too.

Native tiles are coming — the API works today

The Integrations hub lists Zapier and Make as roadmap tiles. Until those one-click tiles land, the recipes above (a key + the catch/HTTP modules) are the supported way to connect them, and they'll keep working after the tiles ship.

Troubleshooting

Every API call comes back 401 unauthorized.

Why: The key is missing, mistyped, revoked, expired, or sent in the wrong header.

Fix: Send it exactly as Authorization: Bearer svk_live_… (no quotes, no extra spaces). Check the message: "This API key has expired" means it reached its expiry date. Otherwise confirm the key isn't showing a Revoked badge on Settings → API keys; if unsure, mint a new one and swap it in.

Reads work but creating anything returns 403 insufficient_scope.

Why: The key was minted Read-only, so it can GET but never write.

Fix: Mint a Full access key on Settings → API keys, paste it into the tool, and revoke the read-only one. GET /api/v1/me reports key.scope if you want to confirm which kind you're holding.

The Create key / Revoke buttons are greyed out.

Why: Managing API keys requires the shop.manage permission — the page is read-only for techs.

Fix: Ask an owner or manager to mint or revoke the key, or to upgrade your role on the Team page.

Requests suddenly start returning 429.

Why: You're over 120 requests per minute for that key.

Fix: Back off a few seconds and retry. Page through lists with the cursor instead of refetching, and space out bulk jobs.

Webhooks were subscribed but nothing ever arrives.

Why: The endpoint auto-paused after 10 consecutive failures, or the receiving URL is rejecting the POST.

Fix: Confirm your URL returns a 2xx quickly and re-check your signature verification — while it's still under 10 strikes, a single successful delivery clears the count. Once it auto-pauses it stays off until you re-enable it: hit Resume on the endpoint under Settings → Integrations → Webhooks (which also resets the strike count), or subscribe a fresh URL.

Common questions

Can I limit a key to read-only?

Yes — choose Read-only when you create it. That key can GET every resource and nothing else; any create or delete comes back 403 insufficient_scope. There's still no per-endpoint control, so the rest of the containment story is unchanged: one key per tool, revoked independently.

I made a key read-only and now my Zap is failing with 403.

Zapier and Make need Full access — they subscribe webhooks and create records. Mint a new full-access key on Settings → API keys, paste it into the integration's connection, then revoke the read-only one.

Can I change a key's scope or push back its expiry date?

No — both are fixed when the key is minted, deliberately: quietly widening a live credential is exactly the change nobody notices. Mint a replacement with the access you want, switch the tool over, then revoke the old key.

What happened to my old keys now that scopes exist?

Nothing. Every key that existed before scopes shipped is Full access with no expiry, so all your existing integrations kept working untouched.

I lost the key right after creating it. How do I get it back?

You can't — only a one-way hash is stored, so the plaintext is unrecoverable. Revoke that key on Settings → API keys and mint a fresh one; it takes seconds.

Does creating a webhook subscription need a key too?

Yes. You subscribe by calling POST /api/v1/hooks with your Bearer key. The response returns a whsec_… secret once — store it to verify signatures. Managing the subscription later (list/delete) uses the same key.

Why can't I create a job through the API?

POST /api/v1/jobs returns 501 on purpose — jobs involve sequence numbers, bay-conflict checks and scheduling rules that belong in the app. Create a lead via the API and convert it to a job in the dashboard.

How do I test a key quickly?

Call GET /api/v1/me with the Bearer header. A 200 that names your shop confirms the key works; a 401 means it's wrong, revoked, or missing.

Was this helpful?

Ready to run your shop on Service VIN?

Every feature in these docs is in the free trial — card required, nothing charged for 14 days.