inspecHelp Guides

API

Connect Inspec to your accounting software, automations and other tools with the Inspec API.

Overview

The Inspec API lets other software read your company's projects, suppliers, products, invoices and payments, and add suppliers. Common uses include syncing invoices and payments into your accounting software, building automations with tools like Zapier, and pulling your product library into other design tools.

Need the API to do more?

Email [email protected] and tell us what you want to connect. We add endpoints and fields when studios ask for them, and we're glad to hear what would help.

Keys are either read only or read and write. A read-only key can't change anything in your account. A read and write key can also create suppliers. The API doesn't include the contents of your schedules.

All requests go to:

https://api.inspec.design/v1

Authentication

Creating an API key

Only admins can create API keys.

  1. Open API settings - Go to Settings and select API.
  2. Create a key - Click Create key and give it a name that describes where it will be used, for example "Zapier" or "Xero sync".
  3. Choose its access - Choose Read only for tools that only pull data out of Inspec. Choose Read and write only if the tool needs to add data to Inspec. You can't change this later, so create a new key if you need different access.
  4. Copy the key - Copy the key and store it somewhere safe. For security, it's only shown once.

You can revoke a key from the same page at any time, and anything using it will stop working immediately.

Keep your keys secret

Anyone with one of your keys can read your company's data, and a read and write key can also add to it. Don't share keys by email or include them in website code. If a key is exposed, revoke it and create a new one.

Making requests

Send your key in the Authorization header of every request:

curl https://api.inspec.design/v1/me \
  -H "Authorization: Bearer insp_your_key_here"

The API is available to companies with an active subscription or trial.

Each key can make up to 120 requests a minute, shared across all endpoints. If you go over, you'll get a 429 with a Retry-After header.

Conventions

Pagination

List endpoints return a page of results and a cursor for the next page:

{
  "data": [],
  "nextCursor": "cm7x2k9lq0001abcd"
}
ParameterDescription
limitResults per page, from 1 to 100. Defaults to 50.
cursorThe nextCursor from the previous page.
updatedSinceOnly return records updated at or after this time, e.g. 2026-03-01T09:00:00Z. A date on its own, like 2026-03-01, means midnight UTC. Write + in a timezone offset as %2B.

Unknown parameters return a 400, so a typo can't silently change what you get back.

Results are ordered newest first. To get the next page, repeat the request with cursor set to nextCursor. When nextCursor is null, you've reached the last page. If the record a cursor points to is deleted while you're paging, you'll get a 400 invalid_request. Start again from the first page.

Syncing changes

To keep another system up to date, record the time you start each sync and pass it as updatedSince next time. You'll get the records that were created or changed in between.

updatedSince only returns records that are still in the list, so it won't tell you when something leaves it. For example, a deleted product or supplier, a deleted project along with its invoices and payments, or an invoice reopened as a draft along with its payments. To catch these, run a full sync without updatedSince from time to time and remove anything that's no longer returned.

Money

Amounts are whole numbers in the smallest unit of the currency (cents or pence), alongside a lowercase currency code. For example, "total": 125050 with "currency": "aud" is $1,250.50 AUD. Product costs and RRPs are always before tax.

Dates

  • Timestamps such as createdAt and updatedAt are in UTC, e.g. 2026-03-14T09:30:00.000Z.
  • Document dates such as an invoice date or payment date are written as YYYY-MM-DD in your company's timezone, so they match the dates on your PDFs.

Creating records

Creating a record needs a read and write key. Send the record as a JSON object with a Content-Type: application/json header. Fields you leave out are left empty, and unknown fields return a 400, the same as unknown parameters.

A successful request returns 201 with the new record, in the same shape you'd get from fetching it.

Some records must be unique, for example supplier names. Creating a duplicate returns a 409 conflict with the existing record's id in details.id. This makes it safe to retry a request that timed out: if the first attempt went through, the retry tells you where to find it.

Errors

Errors return a matching HTTP status and a body like this:

{
  "error": {
    "code": "not_found",
    "message": "Invoice not found"
  }
}
StatusCodeMeaning
400invalid_requestA parameter or field is invalid or unknown. details lists each problem.
401unauthorizedThe key is missing, invalid or revoked.
403subscription_requiredYour company doesn't have an active subscription or trial.
403insufficient_scopeThe key doesn't have access to this endpoint, for example a read-only key creating a record.
404not_foundThe record or endpoint doesn't exist.
405method_not_allowedThe endpoint doesn't support this HTTP method.
409conflictThe record already exists. details.id is the existing record's id.
429rate_limitedToo many requests. Wait for the number of seconds in the Retry-After header.
500internal_errorSomething went wrong on our side. We're notified automatically.

What's not included

Demo projects and invoices, and deleted projects, are never returned. Draft invoices are only returned when you ask for them (see Invoices).

Endpoints

Company

GET /v1/me

Returns the company your key belongs to. This is a good way to check that a key works.

{
  "company": {
    "id": "cm1a2b3c40000abcd",
    "name": "Studio North",
    "currency": "aud",
    "timezone": "Australia/Sydney"
  },
  "apiKey": {
    "id": "cm9z8y7x60000abcd",
    "name": "Zapier",
    "scopes": ["read"]
  }
}

scopes is ["read"] for a read-only key and ["read", "write"] for a read and write key.

Projects

GET /v1/projects

Lists your projects. Also accepts status (active or archived).

GET /v1/projects/{id}

Returns a single project.

{
  "id": "cm2p0000000000abcd",
  "name": "Bondi House",
  "status": "active",
  "currency": "aud",
  "address": {
    "line1": "12 Campbell Parade",
    "line2": null,
    "city": "Bondi Beach",
    "state": "NSW",
    "postcode": "2026",
    "country": "Australia"
  },
  "client": {
    "name": "Jane Smith",
    "emails": ["[email protected]"],
    "phone": "+61 400 000 000",
    "address": {
      "line1": "12 Campbell Parade",
      "line2": null,
      "city": "Bondi Beach",
      "state": "NSW",
      "postcode": "2026",
      "country": "Australia"
    }
  },
  "startDate": "2026-02-01",
  "endDate": null,
  "createdAt": "2026-01-15T03:12:45.000Z",
  "updatedAt": "2026-03-02T22:01:10.000Z"
}

Suppliers

GET /v1/suppliers

Lists your suppliers.

GET /v1/suppliers/{id}

Returns a single supplier.

{
  "id": "cm3s0000000000abcd",
  "name": "Coastal Tiles",
  "email": "[email protected]",
  "phone": "02 9000 0000",
  "website": "https://coastaltiles.com",
  "notes": null,
  "tradeDiscountPercentage": 15,
  "address": {
    "line1": "4 Harbour Road",
    "line2": null,
    "city": "Alexandria",
    "state": "NSW",
    "postcode": "2015",
    "country": "Australia"
  },
  "contacts": [
    {
      "id": "cm3c0000000000abcd",
      "name": "Sam Lee",
      "email": "[email protected]",
      "phone": null,
      "notes": "Trade account manager"
    }
  ],
  "tags": ["Stone", "Tiles"],
  "createdAt": "2025-11-04T01:20:00.000Z",
  "updatedAt": "2026-02-18T05:44:31.000Z"
}

POST /v1/suppliers

Creates a supplier. Needs a read and write key. The body uses the same fields as the supplier above, and the response is the new supplier.

curl https://api.inspec.design/v1/suppliers \
  -H "Authorization: Bearer insp_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Coastal Tiles",
    "email": "[email protected]",
    "tradeDiscountPercentage": 15,
    "address": { "city": "Alexandria", "state": "NSW" },
    "contacts": [{ "name": "Sam Lee", "email": "[email protected]" }],
    "tags": ["Stone", "Tiles"]
  }'
FieldDescription
nameRequired. Up to 100 characters, and unique within your company.
email, phone, website, notesOptional.
tradeDiscountPercentageOptional whole number from 0 to 100.
addressOptional. Any of line1, line2, city, state, postcode and country.
contactsOptional list of up to 50 contacts, each with a name and optional email, phone and notes.
tagsOptional list of up to 20 tag names. Tags that don't exist yet are created.

If a supplier with the same name already exists, you'll get a 409 conflict with its id in details.id. Names are matched ignoring case, so "Coastal Tiles" and "coastal tiles" count as the same supplier.

Products

GET /v1/products

Lists the products in your product library. Also accepts supplierId.

GET /v1/products/{id}

Returns a single product.

{
  "id": "cm6d0000000000abcd",
  "name": "Carrara Hexagon Mosaic",
  "supplier": { "id": "cm3s0000000000abcd", "name": "Coastal Tiles" },
  "url": "https://coastaltiles.com/carrara-hexagon",
  "notes": null,
  "currency": "aud",
  "cost": 8950,
  "rrp": 12900,
  "imageUrl": "https://media.inspec.design/example.jpg",
  "isFavourite": true,
  "tags": ["Bathroom"],
  "fields": [
    { "name": "Finish", "type": "select", "value": "Honed", "text": "Honed" },
    {
      "name": "Size",
      "type": "dimension",
      "value": { "width": 300, "depth": 10, "height": 300, "unit": "mm" },
      "text": "W 300mm × D 10mm × H 300mm"
    }
  ],
  "attachments": [
    {
      "id": "cm6a0000000000abcd",
      "name": "Spec sheet.pdf",
      "url": "https://media.inspec.design/spec-sheet.pdf",
      "size": 482113
    }
  ],
  "createdAt": "2026-01-20T07:00:00.000Z",
  "updatedAt": "2026-01-20T07:00:00.000Z"
}

Each field has a text version of its value, formatted with your company's measurement unit. The value depends on the field's type:

TypeValue
text, select, supplierA string, or null.
multi_selectA list of strings.
propertyAn object of property names to values, e.g. { "Colour": "White" }.
dimensionwidth, depth and height in millimetres (each can be null).
imageurl, name and caption.
pricequantity and unitCost (in the smallest currency unit, before tax).

text is empty for image, price and supplier fields.

Invoices

GET /v1/invoices

Lists invoices and credit notes. Only sent ones are returned unless you set status. Also accepts:

ParameterDescription
projectIdOnly invoices for this project.
typeinvoice or credit_note.
paymentStatusunpaid, paid or on_account.
statussent (the default), draft or archived. Archived invoices were cancelled before being sent.

GET /v1/invoices/{id}

Returns a single invoice, including its payments.

{
  "id": "cm4i0000000000abcd",
  "number": 42,
  "displayNumber": "INV-0042",
  "type": "invoice",
  "kind": "itemised",
  "status": "sent",
  "paymentStatus": "paid",
  "reference": "Kitchen joinery",
  "invoiceDate": "2026-03-01",
  "project": { "id": "cm2p0000000000abcd", "name": "Bondi House" },
  "currency": "aud",
  "subtotal": 1000000,
  "tax": 100000,
  "total": 1100000,
  "creditApplied": 0,
  "amountDue": 1100000,
  "taxBreakdown": [{ "name": "GST", "percentage": 10, "amount": 100000 }],
  "createdAt": "2026-02-27T23:15:00.000Z",
  "updatedAt": "2026-03-10T04:02:12.000Z",
  "payments": [
    {
      "id": "cm5p0000000000abcd",
      "invoice": { "id": "cm4i0000000000abcd", "displayNumber": "INV-0042" },
      "projectId": "cm2p0000000000abcd",
      "date": "2026-03-10",
      "method": "Bank transfer",
      "notes": null,
      "origin": "standard",
      "currency": "aud",
      "amount": 1100000,
      "isReversed": false,
      "reversedAt": null,
      "createdAt": "2026-03-10T04:02:12.000Z",
      "updatedAt": "2026-03-10T04:02:12.000Z"
    }
  ]
}
  • kind is itemised, instalment or design_fees.
  • amountDue is the total minus any creditApplied from the client's credit on file.
  • Credit notes have negative amounts and a displayNumber like CN-0003. A paid credit note was refunded to the client, and an on_account credit note was kept as credit on the client's account.
  • Drafts are still being prepared, so their lines and totals can change until they're sent. Avoid importing them into your accounting software. A draft's totals can also change without its updatedAt changing, for example when the project's tax rate or fees are edited, so fetch drafts again rather than relying on updatedSince.

Payments

GET /v1/payments

Lists client payments against sent invoices. Also accepts invoiceId and projectId. Each payment has the same shape as in the invoice example above.

  • amount is the cash the payment settled. Refunds of credit notes are negative.
  • origin is standard for payments you recorded, credit_on_file when the invoice was paid from the client's credit, and on_account when a credit note was kept on account. Only standard payments move money, so the others have an amount of 0.
  • Reversed payments are included with isReversed: true and an amount of null. Leave them out when adding up totals. Reopening an invoice as a draft reverses its payments and moves them out of this list, so they only appear on the invoice itself.


On this page