BigShop API
Build powerful integrations with the BigShop Truck Repair Management Platform.
https://api.bigshopai.com/v1
Conventions
Money: cents is canonical
All monetary fields are returned in both dollars and cents (e.g. totalAmount in dollars, totalAmountCents in integer cents). Cents is the source of truth — the dollar form exists for backward compatibility and is derived. When sending money in a request body, prefer the *Cents field; it sidesteps floating-point rounding bugs.
Pagination: cursor, not offset
List endpoints return up to limit items plus a lastEvaluatedKey cursor when more pages exist. Pass that cursor back as the lastEvaluatedKey query param to fetch the next page. Some queries are capped server-side; in that case the response carries truncated: true and the UI displays a banner.
Tenant isolation
API keys are scoped to a single tenant; tenant is resolved server-side from the key. You cannot read or write across tenants. Requests for resources owned by a different tenant return 404 Not Found (not 403) so the API does not leak whether a given ID exists.
PUT vs PATCH
Update endpoints accept both PUT and PATCH — the server treats them identically (partial update; only fields you include are changed). The docs use PATCH consistently because that matches the semantics; PUT works as an alias if your HTTP client doesn't support PATCH well.
Errors
Error responses are JSON: { "error": "<short, user-safe message>", "requestId": "<id>" }. Internal exception messages are never leaked — include the requestId when reporting issues.
Authentication
Include your API key in the Authorization header with the
ApiKey prefix:
curl -X GET "https://api.bigshopai.com/v1/customers" \
-H "Authorization: ApiKey YOUR_API_KEY"
Read-only keys
When you create a key in Admin → API Keys you choose its access.
A read-only key can call GET endpoints only: any write is refused
with 403 before it reaches BigShop, credentials and sharing links are never returned,
and employee pay and commission endpoints stay hidden unless the key was created with pay data included.
A full key acts as a shop administrator. Keys created through
POST /v1/api-keys without accessLevel are full.
Use read-only keys for AI agents and dashboards.
Each GET operation in the
OpenAPI spec carries
x-read-only-key:
allowed, with-includePayData or
never. See
Connect an AI agent and the
Reports & Digests reference.
Error Codes
| Code | Description |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found — resource does not exist or belongs to a different tenant. The API returns 404 (not 403) for cross-tenant access to avoid leaking whether the ID exists. |
| 409 | Conflict — optimistic-concurrency clash on a versioned resource (e.g. service-block PATCH with a stale version). Re-fetch and retry. |
| 429 | Rate Limited |
| 500 | Server Error |
❤️ Health Check
Check the API status and availability.
/v1/health
Returns the current health status of the API.
🏢 Customers
Manage customer records for your repair shop.
/v1/customers
List all customers with optional filtering and pagination.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| limit | integer | Max results (default: 50) | |
| lastEvaluatedKey | string | Cursor from a prior response's lastEvaluatedKey field. Pass it back to fetch the next page (cursor-based pagination — not offset). |
|
| search | string | Search by name or email |
/v1/customers
Create a new customer record.
Request Body
| Field | Type | Description | Value |
|---|---|---|---|
| name | string | Customer name | |
| type | string | company | individual | |
| string | Contact email | ||
| phone | string | Phone number |
/v1/customers/{id}
Retrieve a single customer record.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Customer ID |
/v1/customers/{id}
Update a customer record. Only fields you include are changed; omitted fields are left untouched.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | Customer ID | |
| name | body | string | Customer name | |
| body | string | Contact email | ||
| phone | body | string | Phone number |
/v1/customers/{id}
Soft-delete a customer. The record is hidden from list endpoints but kept on the underlying row so historical work orders / invoices keep resolving the customer name.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Customer ID |
🚛 Vehicles
Manage vehicle records linked to customers.
/v1/vehicles
List all vehicles with optional filtering and search capabilities.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| customerId | string | Filter by customer ID | |
| limit | integer | Max results (default: 100) | |
| search | string | General search across all fields | |
| vin | string | Search by VIN (partial match) | |
| licensePlate | string | Search by license plate | |
| unitNumber | string | Search by unit number | |
| make | string | Search by vehicle make | |
| model | string | Search by vehicle model | |
| year | string | Search by model year | |
| vehicleType | string | Filter by vehicle type | |
| includeArchived | boolean | Include archived vehicles (default: false) |
/v1/vehicles
Create a new vehicle record.
Request Body
| Field | Type | Description | Value |
|---|---|---|---|
| customerId | string | Owner customer ID | |
| vin | string | Vehicle Identification Number | |
| licensePlate | string | License plate number | |
| make | string | Vehicle make (e.g., Peterbilt) | |
| model | string | Vehicle model | |
| year | integer | Model year | |
| mileage | integer | Current mileage |
/v1/vehicles/{id}
Retrieve a single vehicle record.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Vehicle ID |
/v1/vehicles/{id}
Update a vehicle record. Only fields you include are changed.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | Vehicle ID | |
| licensePlate | body | string | License plate number | |
| mileage | body | integer | Updated mileage | |
| unitNumber | body | string | Fleet unit number | |
| nickname | body | string | Display nickname |
/v1/vehicles/{id}
Soft-delete a vehicle. The record is hidden from list endpoints; historical work orders keep resolving the vehicle display name.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Vehicle ID |
📝 Service Requests
Programmatically submit a service request — the same intake your customers complete via the Smart Check-In link/QR form. Requests land in the shop's Service Requests inbox with status new, where staff review and convert them to work orders. Only problemData.description is required (min 5 characters); a matching customer phone or vehicle VIN is auto-linked for the convert step.
/v1/service-requests
Create a service request. Returns 201 with the new request's id, requestNumber (e.g. SR-4F9A2C), and status. Pass a stable clientRequestId to make retries idempotent — a repeat within a short window returns the same request with duplicate: true.
Request Body
| Field | Type | Description | Value |
|---|---|---|---|
| problemData.description | string | What's wrong (min 5 chars) | |
| problemData.urgency | string | low | medium | high | urgent (default medium) | |
| customerInfo.companyName | string | Customer / fleet name | |
| customerInfo.phone | string | Auto-links to an existing customer if matched | |
| vehicleInfo.vin | string | Auto-links to an existing vehicle if matched | |
| vehicleInfo.unitNumber | string | Fleet unit number | |
| clientRequestId | string | Idempotency key for safe retries |
Also accepted: customerInfo.firstName/lastName/email/dotNumber/customerType, vehicleInfo.year/make/model/plate/mileage, problemData.occurrenceCondition/duration.
📋 Work Orders
Manage repair work orders and service tickets.
/v1/work-orders
List all work orders with filtering options.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| status | string | estimate | approved | in_progress | quality_check | ready_for_pickup | invoiced | paid | completed | |
| customerId | string | Filter by customer | |
| vehicleId | string | Filter by vehicle | |
| limit | integer | Max results (default: 100). Card-level strip applied at ≥100 unless fullDetails=true. |
|
| fullDetails | boolean | Return full services / line-item arrays even past the 100-item card-strip threshold (used by reports). |
|
| includeArchived | boolean | Set true to include archived work orders. Omitted, the endpoint returns only active orders — archiving (which happens automatically on full payment for most shops) removes an order from the active set, so completed/invoiced history is excluded by default. |
|
| startDate | string | Start of range, YYYY-MM-DD. Pair with endDate. |
|
| endDate | string | End of range, YYYY-MM-DD (inclusive). |
|
| dateField | string | Which date the range filters on — e.g. createdAt or invoicedAt. |
|
| documentType | string | work_order | estimate | invoice |
|
| search | string | Free-text match on order number, customer, vehicle (incl. VIN & unit number). | |
| unpaidOnly | boolean | Only orders with a balance due. paidOnly=true is the inverse. |
|
| sortField | string | Sort column (default createdAt). Pair with sortDirection=asc|desc. |
|
| lastEvaluatedKey | string | Pagination cursor — pass back the value returned by the previous page. |
includeArchived=true, then page with
lastEvaluatedKey. For very large accounts, walk it in
startDate/endDate windows rather than one continuous pass.
Unrecognized query parameters are ignored (not rejected), so a misspelled filter silently returns unfiltered data — check your parameter names against this table.
/v1/work-orders/{id}
Retrieve a single work order with fully assembled service blocks, line items (labor / parts / tires / materials / fees), and resolved customer / vehicle names.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Work order ID |
/v1/work-orders
Create a new work order.
Request Body
| Field | Type | Description | Value |
|---|---|---|---|
| customerId | string | Customer ID | |
| vehicleId | string | Vehicle ID | |
| complaint | string | Customer complaint description | |
| priority | string | low | medium | high | urgent | |
| assignedTo | string | Technician user ID |
/v1/work-orders/{id}
Update top-level work-order fields (status, priority, name, description, due date, assigned technicians, etc.). For per-service-block edits use the /services/{svcId} endpoints below; this route does not overwrite the nested services array.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | Work order ID | |
| status | body | string | estimate | approved | in_progress | quality_check | ready_for_pickup | invoiced | paid | completed | |
| priority | body | string | low | medium | high | urgent | |
| name | body | string | Display name | |
| dueDate | body | string | ISO-8601 date or datetime |
/v1/work-orders/{id}
Soft-delete a work order. Use the dedicated /permanent-delete endpoint (admin only) for hard deletion of test data; soft-deleted records can be restored via /restore.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Work order ID |
Service Blocks & Line Items
Granular endpoints for editing the services[] array on a work order. Each service block carries its own version — PATCH requests must echo the version they read; the server bumps it on success and returns 409 on stale writes.
/v1/work-orders/{woId}/services/{svcId}
Update a single service block (name, description, labor entries, parts, tires, financials). Pass version from your last read; on conflict the server returns 409 and the latest stored version.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| woId | path | string | Work order ID | |
| svcId | path | string | Service block ID | |
| version | body | integer | Version you last read. Server compares and rejects with 409 if the stored version is higher. | |
| name | body | string | Service name | |
| description | body | string | Service description / notes | |
| authorizationStatus | body | string | approved | declined | pending |
/v1/work-orders/{woId}/services/{svcId}
Remove a service block (and its labor / parts / tires / materials line items) from a work order. Totals on the work order are recomputed server-side.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| woId | string | Work order ID | |
| svcId | string | Service block ID |
/v1/work-orders/{woId}/services/{svcId}/items/{itemId}
Update a single line item (labor, part, tire, material, fee) inside a service block. Use this for small edits like changing labor status or part quantity without re-PATCHing the whole service block.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| woId | path | string | Work order ID | |
| svcId | path | string | Service block ID | |
| itemId | path | string | Line item ID | |
| kind | body | string | labor | part | tire | material | fee | |
| status | body | string | For labor: pending | in_progress | paused | completed | |
| quantity | body | number | For parts/tires/materials | |
| unitPriceCents | body | integer | Price per unit in cents (canonical money form) |
/v1/work-orders/{woId}/services/{svcId}/items/{itemId}
Remove a single line item from a service block.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| woId | path | string | Work order ID | |
| svcId | path | string | Service block ID | |
| itemId | path | string | Line item ID | |
| kind | body | string | labor | part | tire | material | fee |
📦 Inventory
Manage parts and supplies inventory.
/v1/inventory
List all inventory items.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| category | string | Filter by category | |
| lowStock | boolean | Show only low stock items | |
| search | string | Search by name or SKU |
/v1/inventory/receiving
Push a dealer invoice you have already parsed. One call creates a purchase order (or merges into an existing one) for the invoice, matches or creates the vendor, and links each line to the part you already stock (exact part number). Send up to 100 lines per request; split larger invoices.
Modes
"draft"(default) — creates or updates a draft purchase order for a person to review. No stock changes until it is received in BigShop, or you callPOST /v1/purchase-orders/{id}/receivewith{"receivedAll": true}. Vendor matching is relaxed in draft mode — if a vendor is missing or ambiguous, the draft PO is still created and flagged for review."receive"— creates/updates the purchase order and receives every line immediately into inventory. Existing parts get stock and cost updated; unknown part numbers are created as new inventory items. A verified vendor is required.
Request body
{
"purchaseOrderNumber": "PO-104",
"vendor": { "name": "Freightliner of Dallas", "accountNumber": "A-9", "createIfMissing": true },
"invoiceNumber": "INV-1042",
"invoiceDate": "2026-10-01",
"mode": "draft",
"note": "Truck 12 repair",
"lines": [
{ "partNumber": "A-100", "description": "Oil filter", "quantity": 2, "unitCostCents": 1250, "vendorPartNumber": "FL-A100" }
]
}
purchaseOrderId/purchaseOrderNumber(optional): if the shop already created a PO (e.g. phone order placed earlier), pass either one. Invoice lines will update matching parts or append new lines on that PO instead of creating a duplicate order.vendor: sendid(an existing vendor) orname. A name is matched to an existing vendor (case and “Inc.”/“LLC” are ignored); if nothing matches, a vendor is created unlesscreateIfMissingisfalse. Indraftmode, vendor is optional — unlinked or ambiguous vendors produce a draft PO with a warning so the reviewer can select the vendor in BigShop. Inreceivemode, an ambiguous name returns409 AMBIGUOUS_VENDORwith candidates.invoiceNumber(required) andinvoiceDate(optional,YYYY-MM-DD).unitCostCentsmust be a whole number of cents (1250= $12.50). Decimals are rejected, not rounded.quantitymust be greater than 0.- Each part number may appear once per request.
Idempotency and duplicates
Send an Idempotency-Key header (8–128 characters, e.g. a UUID). Retrying with the same key returns the original response instead of creating a second order; reusing a key with a different body returns 422. Independently, the same invoiceNumber from the same vendor can only be imported once through this endpoint: a repeat returns 409 DUPLICATE_INVOICE with the existing purchase order.
Example
curl -X POST "https://api.bigshopai.com/v1/inventory/receiving" \
-H "Authorization: ApiKey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 00000000-0000-0000-0000-000000000000" \
-d '{
"purchaseOrderNumber": "PO-104",
"vendor": { "name": "Freightliner of Dallas" },
"invoiceNumber": "INV-1042",
"mode": "draft",
"lines": [
{ "partNumber": "A-100", "description": "Oil filter", "quantity": 2, "unitCostCents": 1250 }
]
}'
Response (201)
{
"mode": "draft",
"status": "pending_review",
"purchaseOrder": {
"id": "…",
"number": "PO-104",
"status": "draft",
"vendorId": "…",
"vendorName": "Freightliner of Dallas",
"vendorInvoiceNumber": "INV-1042",
"totalCostCents": 2500,
"lineCount": 1,
"linkedExistingPo": true
},
"vendor": { "id": "…", "name": "Freightliner of Dallas", "created": false, "matchedBy": "name" },
"lines": [
{ "index": 0, "partNumber": "A-100", "quantity": 2, "unitCostCents": 1250,
"match": "existing", "inventoryPartId": "…", "outcome": "pending_review" }
],
"warnings": [],
"partialFailure": false,
"failures": [],
"nextStep": "Review the draft purchase order in BigShop and receive it, …"
}
Line outcome is pending_review (draft), or in receive mode stocked_existing, created_part, skipped_duplicate, failed or not_stocked. warnings may include VENDOR_UNLINKED, AMBIGUOUS_VENDOR, VENDOR_CREATED, AMBIGUOUS_PART_NUMBER and DUPLICATE_INVOICE_LINE.
Partial failure: if the purchase order was created or updated but receiving failed (or a line failed), the response is still 201 with "partialFailure": true and a failures list. Do not resend the invoice — the purchase order exists; check it in BigShop.
Errors
| 400 VALIDATION_ERROR | Body is invalid; details lists every bad field. Nothing was created. |
| 404 PO_NOT_FOUND | Specified purchaseOrderId or purchaseOrderNumber was not found. |
| 404 VENDOR_NOT_FOUND | Receive mode: unknown vendor.id, or no name match with createIfMissing: false. |
| 409 PO_ALREADY_RECEIVED | The specified purchase order is already received, fulfilled, closed, or cancelled. |
| 409 VENDOR_MISMATCH | The specified purchase order belongs to a different vendor than requested. |
| 409 AMBIGUOUS_VENDOR | Receive mode: vendor name matches several existing vendors; resend with vendor.id. |
| 409 DUPLICATE_INVOICE | This vendor's invoice was already imported (existingPurchaseOrderId). |
| 409 INVOICE_IN_PROGRESS | Another request is importing it right now; check before retrying. |
| 422 AMBIGUOUS_PART_NUMBER | Receive mode only: a part number matches several inventory items. Use draft mode or fix the duplicates. |
| 500 RECEIVING_FAILED | The message says whether anything was created. Before the order exists, retrying is safe. |
PATCH /v1/inventory/{id}. Duplicate-invoice detection covers invoices imported through this endpoint. Parts only (not materials or tires). Part numbers are matched exactly (case-insensitive); A-100 and A100 are different parts.
📑 Purchase Orders
Manage parts purchasing, vendor orders, line items, and inventory receiving. AI agents can list orders with status=ordered to inspect open orders and match incoming dealer invoices.
/v1/purchase-orders
List all purchase orders for the shop. Filter by status (e.g. ordered), vendor ID, or date range.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| status | string | Filter by status: draft | ordered | partially_received | received | cancelled | |
| vendorId | string | Filter by vendor ID | |
| limit | number | Max POs to return (default 100) | |
| dateFrom | string | Start date (YYYY-MM-DD) | |
| dateTo | string | End date (YYYY-MM-DD) |
/v1/purchase-orders/{id}
Retrieve details of a single purchase order including line items (parts), received counts, unit costs, and audit log.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Purchase order ID |
/v1/purchase-orders
Create a new purchase order. Supports optional Idempotency-Key header to prevent duplicate orders on retry.
Body Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| vendorName | string | Vendor name | |
| status | string | Initial status (draft or ordered) | |
| note | string | Optional purchase order note |
/v1/purchase-orders/{id}/receive
Receive line items on an existing purchase order directly into shop inventory. Updates stock levels, logs receipt entries, and marks the purchase order received.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | Purchase order ID | |
| receivedAll | body | boolean | Receive all outstanding items (default true) |
🔍 Inspections
Manage vehicle inspection records.
/v1/inspections
List all inspection records.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| vehicleId | string | Filter by vehicle | |
| workOrderId | string | Filter by work order | |
| status | string | passed | failed | pending |
/v1/inspections/{id}
Retrieve a single inspection with all its line items (passed / failed / NA results, photos, technician notes).
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Inspection ID |
/v1/inspections/{id}
Update top-level inspection fields (status, technician, notes). Use the dedicated /items/{itemId} endpoint to edit individual checklist items.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | Inspection ID | |
| status | body | string | passed | failed | pending | |
| notes | body | string | Inspector notes / summary | |
| technicianId | body | string | Inspecting technician's user ID |
/v1/inspections/{id}
Soft-delete an inspection. Photos remain in S3 (lifecycle policy handles eventual cleanup); the record is hidden from list endpoints.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | Inspection ID |
👥 Users
Manage shop users and technicians.
/v1/users
List all users in the shop.
Query Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| role | string | admin | manager | technician | service_writer | |
| active | boolean | Filter by active status |
/v1/users/me
Get the current authenticated user's profile.
🛠️ Admin
Tenant-administrator endpoints for user management. All routes are gated by the settings.users permission — non-admin API keys get 403 Forbidden. The non-admin /v1/users list above is read-only and tenant-scoped; everything below requires admin.
/v1/admin/users, not /v1/users.
/v1/admin/users
Create a new shop user. The user receives an email invite with a temporary password unless password is set explicitly.
Request Body
| Field | Type | Description | Value |
|---|---|---|---|
| string | User's email (login) | ||
| name | string | Full name | |
| role | string | admin | manager | technician | service_writer | |
| phone | string | Phone number |
/v1/admin/users/{id}
Retrieve a user by ID, including admin-only fields like isActive, last login, and permission overrides.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | User ID |
/v1/admin/users/{id}
Update user fields (name, role, phone). Admin-only. To change a password use /set-password instead.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | User ID | |
| name | body | string | Display name | |
| role | body | string | admin | manager | technician | service_writer |
/v1/admin/users/{id}
Soft-delete a user. The user can no longer log in, but historical work-order assignments still resolve their name. Use /permanent-delete for irreversible removal of test accounts.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | User ID |
Account Actions
Targeted admin actions on a specific user account. Each is a single-purpose POST that returns the updated user.
/v1/admin/users/{id}/set-password
Force-set a user's password (admin-initiated reset). The new password is hashed server-side; the plaintext is never stored or echoed back.
Path & Body Parameters
| Param | In | Type | Description | Value |
|---|---|---|---|---|
| id | path | string | User ID | |
| password | body | string | New password (min 8 chars) |
/v1/admin/users/{id}/activate
Re-enable a previously deactivated user. The user can log in again immediately; historical assignments are unchanged.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | User ID |
/v1/admin/users/{id}/deactivate
Disable a user without deleting them. The user can no longer log in or appear in technician pickers, but their historical activity is preserved. Reverse with /activate.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | User ID |
/v1/admin/users/{id}/permanent-delete
Hard-delete a user record. Irreversible. Use this only for test accounts or compliance-mandated deletion (right-to-be-forgotten); historical work-order assignments will fall back to displaying just the user ID.
DELETE /v1/admin/users/{id} for normal offboarding. This permanent variant cannot be undone.
Path Parameters
| Param | Type | Description | Value |
|---|---|---|---|
| id | string | User ID |
🔔 Webhooks
Receive real-time push notifications for changes in BigShop. Every webhook payload is signed with HMAC SHA-256 so your agents and services can verify authenticity.
🔐 Webhook Security & Signatures
BigShop sends an X-BigShop-Signature-256 header formatted as t=<timestamp>,v1=<signature>. To verify the signature, compute HMAC SHA-256 over <timestamp>.<raw_payload> using your webhook secret (whsec_...).
import hmac, hashlib
def verify_webhook(payload: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=") for p in header.split(","))
timestamp, sig = parts["t"], parts["v1"]
signed = f"{timestamp}.".encode() + payload
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, expected)
const crypto = require('crypto');
function verifyWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
const signed = `${parts.t}.${rawBody}`;
const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
/v1/webhooks
List all configured webhooks for your tenant.
/v1/webhooks
Register a new webhook subscription. Returns the endpoint secret (whsec_...) once upon creation. Use HTTPS URLs in production.
Request Body
{
"name": "ASA Pro Parts Bot",
"url": "https://integration.asarepair.com/api/webhooks/bigshop",
"events": [
"inventory.created",
"inventory.updated",
"purchase_order.received"
]
}
Response (201 Created)
{
"data": {
"id": "7f8b9a2c-4d3e-4b1a-8c9d-1e2f3a4b5c6d",
"name": "ASA Pro Parts Bot",
"url": "https://integration.asarepair.com/api/webhooks/bigshop",
"secret": "whsec_0000000000000000000000000000000000000000",
"events": ["inventory.created", "inventory.updated", "purchase_order.received"],
"isActive": true,
"createdAt": "2026-10-04T01:23:45.678Z",
"warning": "Store this secret securely. It will not be shown again."
}
}
Available Webhook Events
| Event | Category | Description |
|---|---|---|
| inventory.created | Inventory | New inventory item created |
| inventory.updated | Inventory | Inventory item edited in BigShop (details, cost, pricing or a manual quantity change) |
| purchase_order.created | Purchasing | New purchase order draft created |
| purchase_order.received | Purchasing | Purchase order items received into stock |
| work_order.created | Work Orders | New work order opened |
| work_order.status_changed | Work Orders | Work order transitioned to new status/column |
| invoice.created | Billing | Customer invoice finalized |
| payment.received | Billing | Payment recorded on work order or invoice |
| purchase_order.updated | Purchasing | Triggered when a PO is modified or lines change. |
| work_order.updated | Work Orders | Triggered when work order details or lines change. |
| customer.created | Customers | Triggered when a new customer is added. |
| vehicle.created | Customers | Triggered when a new vehicle is added. |
| service_request.created | Service Requests | Triggered when a digital check-in or customer service request is submitted. |
| service_request.converted | Service Requests | Triggered when a service request is accepted and converted to a work order. |
| sales_order.created | Counter Sales | Triggered when a new parts counter sale / sales order is drafted. |
| sales_order.completed | Counter Sales | Triggered when a parts counter sale is paid and inventory is deducted. |
| sales_order.refunded | Counter Sales | Triggered when a counter sale item or payment is returned/refunded. |
| digest.ready | Digests | Triggered when a daily or weekly Smart Digest has been generated. Carries the digest date and headline only; fetch the full digest from GET /v1/digests/{businessDate}. |
| webhook.test | System | Ping event sent from test endpoint |