The Pixelvile API.

Read and change a business’s bookings, customers, invoices and enquiries from your own software, and hear about each one as it happens. For businesses on Pixelvile with the Integrations add-on.

Getting started

The owner makes a key in the dashboard under Settings › Integrations › API keys. A key belongs to one business and is shown once. It can do only what was ticked when it was made: read or change bookings, customers, invoices and enquiries.

A key never does more than the person who made it can do in the dashboard today. If their role changes, the key changes with it. If they leave the team, it stops working.

Send the key with every request, over HTTPS:

curl https://pixelvile.com/api/v1/me \
  -H "Authorization: Bearer $PIXELVILE_API_KEY"

Money, times and phone numbers

  • Amounts are whole paise in Indian rupees (₹1,180.00 is 118000). GST rates are basis points (18% is 1800).
  • Booking times are the business’s local time in Kolkata, written 2026-11-09T10:30. Dates are YYYY-MM-DD.
  • Phone numbers come back with the country code, like +919830012345. You can send 10-digit Indian numbers.

Errors and limits

Errors come back as JSON with the HTTP status and a message you can show to a person:

{ "error": { "status": 403, "message": "This key doesn’t have the “customers:read” scope." } }
  • 401: the key is missing, wrong or revoked.
  • 403: the add-on is switched off, or the key’s scopes or its maker’s role don’t allow it.
  • 400, 404, 409: a field is wrong, the record isn’t there, or it clashes with something (a time that’s already booked).
  • 429: too many requests. Each key may make 120 a minute. Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining, and a 429 carries Retry-After in seconds.

Endpoints

This is the full list. Each endpoint goes through the same checks as the dashboard: the business’s add-ons, the person’s role and the same validation. A double booking is refused here too.

Who am I

  • GET /api/v1/me returns the business, the key’s name and scopes, and the rate limit. Works with any key.

Bookings

  • GET /api/v1/bookings?from=2026-11-09&to=2026-11-15 lists bookings in a range of up to two months (by default, the next 7 days). Add staff_id for one calendar. Scope bookings:read.
  • GET /api/v1/bookings/:id returns one booking. Scope bookings:read.
  • POST /api/v1/bookings adds a booking. Send service_id, date, time, customer_name and phone. You can also send staff_id, notes and source: phone, walk_in or staff (the default). Scope bookings:write.
  • PATCH /api/v1/bookings/:id changes the status (confirmed, done, no_show or cancelled) or the notes. Scope bookings:write.
curl -X POST https://pixelvile.com/api/v1/bookings \
  -H "Authorization: Bearer $PIXELVILE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service_id": 12, "date": "2026-11-09", "time": "10:30", "customer_name": "Ananya Sen", "phone": "9830012345"}'
{ "booking": { "id": 481, "status": "confirmed", "start_at": "2026-11-09T10:30", "duration_min": 30,
  "service_id": 12, "service": "Check-up", "staff_id": null, "staff_name": null, "source": "staff",
  "customer": { "id": 77, "name": "Ananya Sen", "phone": "+919830012345" }, "notes": null } }

Customers

  • GET /api/v1/customers?q=ananya&page=1 lists customers, most recent visit first. q searches names, emails and phone numbers. Scope customers:read.
  • GET /api/v1/customers/:id returns one customer and their bookings. Scope customers:read.
  • PATCH /api/v1/customers/:id changes the name, email or notes. Scope customers:write.

Invoices

  • GET /api/v1/invoices?status=sent&page=1 lists invoices. status is draft, sent (unpaid), paid or void. Add month as YYYY-MM, or customer_id for one customer’s. Scope invoices:read.
  • GET /api/v1/invoices/:id returns one invoice with its lines and its public link. Scope invoices:read.
  • POST /api/v1/invoices makes a draft for the owner to check and send from the dashboard. Send customer_id, or customer with a name and phone. Then send items, each with a description, qty and unit_price in paise, and if needed a tax_rate, discount and hsn. Scope invoices:write.
curl -X POST https://pixelvile.com/api/v1/invoices \
  -H "Authorization: Bearer $PIXELVILE_KEY" -H "Content-Type: application/json" \
  -d '{"customer_id": 77, "items": [{"description": "Scaling", "qty": 1, "unit_price": 90000, "tax_rate": 1800}]}'

Enquiries

  • GET /api/v1/enquiries?status=new lists the latest 200 enquiries. status is new, replied or closed. Scope enquiries:read.
  • PATCH /api/v1/enquiries/:id sets the status. Scope enquiries:write.

A key with a :write scope can also read. Nothing can be deleted through the API.

Webhooks

The owner adds an https address under Settings › Integrations › Webhooks and picks the events. As each one happens, we send a POST with a JSON body:

  • booking.created: a booking is made, online or in the dashboard.
  • booking.updated: a booking is moved, marked done or no-show, or given to someone else, or its notes change.
  • booking.cancelled: a booking is cancelled, by the business or by the customer.
  • enquiry.created: an enquiry comes in from the website form.
  • invoice.issued: an invoice gets its number.
  • invoice.paid: an invoice is paid in full.
  • customer.created: a booking or an online order brings in a new customer.
  • order.created: an online order is placed.
POST /your-endpoint
Content-Type: application/json
Pixelvile-Event: booking.created
Pixelvile-Signature: t=1762682400,v1=5f1c…e9

{ "id": "evt_Qm3…", "type": "booking.created", "created": 1762682400, "business": "smile-craft",
  "data": { "id": 481, "status": "confirmed", "start_at": "2026-11-09T10:30", "service": "Check-up",
            "staff_id": null, "customer": { "id": 77, "first_name": "Ananya" } } }

Events carry ids and first names only. They never include phone numbers or messages. If your key allows it, use the API to read more.

Checking the signature

Each webhook has its own signing secret (whsec_…), shown once when the webhook is made. The Pixelvile-Signature header has two parts:

  • t=: the time, in Unix seconds.
  • v1=: the hex HMAC-SHA256, made with the secret, of the time, a full stop and the raw body.

Compare the signature in constant time, and refuse anything older than five minutes:

import crypto from 'node:crypto';
function fromPixelvile(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(',').map(p => p.split('=')));
  const want = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && v1?.length === want.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}

Answers, retries and switching off

  • Answer with any 2xx within 10 seconds. We don’t follow redirects.
  • If you answer anything else, we try again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours. That’s seven tries over about a day. Use the event id to ignore a repeat.
  • After 10 failed tries in a row, we switch the webhook off and email the owner. The dashboard has a delivery log with each answer code, and a Send test button.
  • Addresses must use https on the standard port and be on the public internet. We refuse private, local and IP addresses.

Calendars and accounting

The same add-on gives every team member a private calendar link, an .ics subscription for Google, Apple or Outlook that shows only the bookings they can see. Owners also get Tally Prime and Zoho Books exports under Invoices › Accounting. Neither needs a key.

Questions

Email developers@pixelvile.com. If you’ve found a security problem, see our security page.

Free audit