ApptGuard API
The ApptGuard API lets your external software read and write data in your account. Every action available through the API is something that already exists in your dashboard — the API simply gives your other tools a way to do the same things automatically.
For example, if your website lets customers book appointments, it can use this API to create those appointments directly in your ApptGuard account instead of someone having to enter them manually.
What You Can Do
The API gives you access to the following parts of your account:
- Appointments — read, create, update, and delete appointments
- Clients — read and create client records, update contact information and tags
- Inventory — read stock levels, add or remove stock, set exact quantities
- Quotes — read quotes and their status
- Invoices — read invoices
- Reports — pull appointment stats and weekly product usage breakdowns
- Expenses ★ Elite — create, read, update, and delete business expenses with tax categories and receipt tracking
- Mileage Log ★ Elite — log trips with start/end addresses, times, and mileage
- P&L Summary ★ Elite — pull profit & loss data for any date range, including revenue by client/service and expense breakdown by category
- Offline Pack ★ Elite — download a day's appointments and forms for offline use
The API only exposes your own account's data. It cannot access other businesses' accounts, and it cannot change how the platform itself works.
403 Forbidden with the message "This feature requires the Elite plan."
Get Your API Key
An API key is a long string of characters that proves who you are when your software contacts ApptGuard. Think of it like a password that your application uses instead of you.
- Sign in to your ApptGuard account.
- Go to Settings and click the API tab.
- Click Generate New Key and give it a name (for example, "My Website" or "Zapier").
- Copy the key immediately — it is only shown once. Store it somewhere safe like a password manager or your server's environment variables.
You can create up to 5 active API keys per account. This lets you give separate keys to separate applications, and revoke one without breaking the others.
Authentication
Every request to the API must include your API key in a special header called X-API-Key.
curl https://appt-guard.nihnyx.com/api/v1/ping \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
If your key is valid, the response will be:
{
"ok": true,
"account": "Your Business Name",
"plan": "elite",
"timestamp": "2026-07-01T12:00:00.000Z"
}
The plan field shows which plan your account is on. Elite-only endpoints are only accessible when this value is "elite".
If the key is wrong or has been revoked, you will receive:
{
"error": "Invalid or revoked API key."
}
Base URL
All API endpoints begin with the same base URL:
https://appt-guard.nihnyx.com/api/v1
Elite endpoints follow the same pattern but under the /elite/ path:
https://appt-guard.nihnyx.com/api/elite
Rate Limits
Rate limits scale with your account's plan — higher tiers get more requests per minute, in addition to unlocking more endpoints (see Plan Access below). You can check your current limit at any time via GET /api/apikeys/limits from your dashboard session, or by reading the X-RateLimit info returned on a 429 response.
| Plan | Requests / minute |
|---|---|
| Free | 20 |
| Starter | 60 |
| Growth | 120 |
| Pro | 300 |
| Elite | 600 |
If you exceed your plan's limit, the API responds with HTTP 429:
{
"error": "Rate limit exceeded. Your plan allows 60 requests per minute.",
"yourPlan": "starter",
"limit": 60,
"upgrade": "https://appt-guard.nihnyx.com/pricing/"
}
Rate limits are enforced per API key, independently of which endpoints your plan can access — this applies even to endpoints your plan is allowed to call, so raising your request volume alone can't substitute for a higher plan's endpoint access.
Plan Access
Each endpoint requires a minimum plan tier. Calling an endpoint above your plan returns 403 Forbidden with the plan you'd need — this is enforced server-side on every request, not just hidden in the dashboard UI, so it can't be bypassed by calling the API directly.
| Plan | Adds access to |
|---|---|
| Free | /ping, /account only |
| Starter | + Appointments, Clients, Quotes |
| Growth | + Inventory, Appointment reports, Online payment status |
| Pro | + Invoices, Weekly product reports |
| Elite | + Expenses, Mileage, P&L reports (via /api/elite/*) |
{
"error": "This endpoint requires the Growth ($83/mo) plan or above.",
"yourPlan": "starter",
"upgrade": "https://appt-guard.nihnyx.com/pricing/"
}
Understanding Errors
| Status | Meaning | What to do |
|---|---|---|
| 200 | Success | Read the response body |
| 201 | Created | Read the response for the new item |
| 400 | Bad Request — something in your request is wrong | Read the error field for details |
| 401 | Unauthorized — API key is missing or invalid | Check your X-API-Key header |
| 403 | Forbidden — your plan does not include this endpoint | See Plan Access for the tier you need |
| 404 | Not Found — the record does not exist | Check the ID or phone number you are using |
| 409 | Conflict — record already exists | Use PUT to update instead of POST to create |
| 429 | Too Many Requests — rate limit exceeded | Wait 60 seconds, or upgrade for a higher limit |
| 500 | Server Error | Try again; contact support if it persists |
Tutorial: Your First Request
curl https://appt-guard.nihnyx.com/api/v1/ping \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
You should see a JSON response with your account name, plan, and the current time. The plan field will show "elite" if your account has access to the Elite endpoints.
Tutorial: Managing Appointments
List upcoming appointments
curl "https://appt-guard.nihnyx.com/api/v1/appointments?from=2026-07-01&status=confirmed" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
{
"count": 2,
"data": [
{
"id": "abc123",
"clientName": "Jane Smith",
"phone": "+15551234567",
"date": "2026-07-15",
"time": "10:00",
"service": "HVAC Inspection",
"status": "confirmed"
}
]
}
Create an appointment
curl -X POST https://appt-guard.nihnyx.com/api/v1/appointments \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"clientName":"Jane Smith","phone":"+15551234567","date":"2026-07-15","time":"10:00","service":"HVAC Inspection"}'
Update an appointment
curl -X PUT https://appt-guard.nihnyx.com/api/v1/appointments/abc123 \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"status":"cancelled","notes":"Client called to cancel"}'
Delete an appointment
curl -X DELETE https://appt-guard.nihnyx.com/api/v1/appointments/abc123 \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
Tutorial: Working with Clients
Look up a client by phone
curl "https://appt-guard.nihnyx.com/api/v1/clients/%2B15551234567" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
+ in phone numbers with %2B when placing them in a URL path.Search clients
curl "https://appt-guard.nihnyx.com/api/v1/clients?search=Smith" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
Create a client
curl -X POST https://appt-guard.nihnyx.com/api/v1/clients \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"name":"Jane Smith","phone":"+15551234567","email":"[email protected]","tags":["Residential","VIP"]}'
Tutorial: Tracking Inventory
Get inventory list
curl https://appt-guard.nihnyx.com/api/v1/inventory \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
Adjust stock after a job
curl -X POST https://appt-guard.nihnyx.com/api/v1/inventory/ITEM_ID/adjust \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"type":"remove","qty":1,"note":"Used on Job #47"}'
Adjustment types: add (new stock arrived), remove (used on a job), set (exact count after a physical count).
Check for low stock
curl "https://appt-guard.nihnyx.com/api/v1/inventory?low_stock=true" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
★ Tutorial: P&L & Mileage (Elite)
403 Forbidden on Starter, Growth, and Pro plans. You can check your plan at any time by calling GET /api/v1/ping and reading the plan field.
Log an expense
Use this after purchasing supplies, paying a vendor, or recording any other business cost. The category field maps to IRS Schedule C tax categories so your records are already organized at tax time.
curl -X POST https://appt-guard.nihnyx.com/api/elite/expenses \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"description": "Engine oil and filter",
"amount": 48.50,
"date": "2026-07-01",
"category": "Vehicle & Mileage",
"notes": "Truck #2 oil change before busy season"
}'
Valid category values:
- Advertising
- Insurance
- Legal & Professional
- Materials & Supplies
- Meals & Entertainment
- Office Supplies
- Rent
- Repairs & Maintenance
- Travel
- Utilities
- Vehicle & Mileage
- Wages & Contract Labor
- Other
Log a trip
Record a business trip with start and end address, optional times, and miles. If you do not know the exact mileage, leave miles out — you can update it later.
curl -X POST https://appt-guard.nihnyx.com/api/elite/mileage \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-07-01",
"startAddress": "123 Main St, Memphis, TN",
"endAddress": "456 Oak Ave, Memphis, TN",
"startTime": "08:30",
"endTime": "08:52",
"miles": 7.4,
"purpose": "Client visit — annual pond cleaning"
}'
Pull a P&L summary
Get a complete profit and loss breakdown for any date range. Revenue comes from paid invoices. Expenses come from your logged expense records. The response includes percentage breakdowns by client, service, and expense category.
curl "https://appt-guard.nihnyx.com/api/elite/pnl?from=2026-07-01&to=2026-07-31" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE"
{
"period": { "from": "2026-07-01", "to": "2026-07-31" },
"revenue": 8420.00,
"expenses": 1240.50,
"netProfit": 7179.50,
"invoiceCount": 23,
"expenseCount": 7,
"revenueByClient": [
{ "name": "Greenfield HOA", "amount": 3960.00, "pct": 47.0 },
{ "name": "Jane Smith", "amount": 840.00, "pct": 10.0 }
],
"revenueByService": [
{ "name": "Pond Cleaning", "amount": 3960.00, "pct": 47.0 },
{ "name": "HVAC Inspection", "amount": 2520.00, "pct": 30.0 }
],
"expensesByCategory": [
{ "name": "Vehicle & Mileage", "amount": 372.15, "pct": 30.0 },
{ "name": "Materials & Supplies", "amount": 248.10, "pct": 20.0 }
]
}
Download an offline pack
Use this to grab everything you need for a day of fieldwork — all appointments and form templates — so you can keep working without cell service.
curl "https://appt-guard.nihnyx.com/api/elite/offline-pack?date=2026-07-04" \
-H "X-API-Key: ag_live_YOUR_KEY_HERE" \
-o offline-2026-07-04.json
The -o flag saves the response directly to a file. The pack contains all appointments for that date, all your form templates, and any submissions already collected.
Tutorial: Using JavaScript (Node.js)
Basic setup
// Store in .env — never commit to Git
const API_KEY = process.env.APPTGUARD_API_KEY;
const BASE_V1 = 'https://appt-guard.nihnyx.com/api/v1';
const BASE_EL = 'https://appt-guard.nihnyx.com/api/elite';
async function ag(path, method = 'GET', body) {
const res = await fetch(path, {
method,
headers: {
'X-API-Key': API_KEY,
...(body ? { 'Content-Type': 'application/json' } : {})
},
body: body ? JSON.stringify(body) : undefined
});
if (!res.ok) throw new Error((await res.json()).error);
return res.json();
}
Log an expense (Elite)
const expense = await ag(`${BASE_EL}/expenses`, 'POST', {
description: 'Engine oil and filter',
amount: 48.50,
date: '2026-07-01',
category: 'Vehicle & Mileage',
});
console.log('Logged expense:', expense.id);
Pull this month's P&L (Elite)
const now = new Date();
const from = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-01`;
const today = now.toISOString().slice(0,10);
const pnl = await ag(`${BASE_EL}/pnl?from=${from}&to=${today}`);
console.log(`Revenue: $${pnl.revenue} | Expenses: $${pnl.expenses} | Net: $${pnl.netProfit}`);
// Show top revenue source
const top = pnl.revenueByService[0];
if (top) console.log(`Top service: ${top.name} (${top.pct}% of revenue)`);
Log a trip and list all mileage (Elite)
// Log a new trip
await ag(`${BASE_EL}/mileage`, 'POST', {
date: '2026-07-01',
startAddress: '123 Main St, Memphis, TN',
endAddress: '456 Oak Ave, Memphis, TN',
startTime: '08:30',
endTime: '08:52',
miles: 7.4,
purpose: 'Client visit',
});
// List all trips
const trips = await ag(`${BASE_EL}/mileage`);
const totalMiles = trips.reduce((s, t) => s + (t.miles || 0), 0);
console.log(`Total miles logged: ${totalMiles.toFixed(1)}`);
Tutorial: Using Python
import requests, os
API_KEY = os.environ.get('APPTGUARD_API_KEY')
BASE_V1 = 'https://appt-guard.nihnyx.com/api/v1'
BASE_EL = 'https://appt-guard.nihnyx.com/api/elite'
HEADERS = {'X-API-Key': API_KEY}
Log an expense (Elite)
r = requests.post(f'{BASE_EL}/expenses', headers=HEADERS, json={
'description': 'Engine oil and filter',
'amount': 48.50,
'date': '2026-07-01',
'category': 'Vehicle & Mileage',
})
r.raise_for_status()
print('Created expense:', r.json()['id'])
Pull a P&L summary (Elite)
r = requests.get(f'{BASE_EL}/pnl', headers=HEADERS,
params={'from': '2026-07-01', 'to': '2026-07-31'})
r.raise_for_status()
pnl = r.json()
print(f"Revenue: ${pnl['revenue']:,.2f}")
print(f"Expenses: ${pnl['expenses']:,.2f}")
print(f"Net: ${pnl['netProfit']:,.2f}")
for row in pnl['revenueByService']:
print(f" {row['name']}: {row['pct']}% of revenue")
Reference: Meta
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/ping | Verify your API key. Returns account name, plan, and server time. |
| GET | /api/v1/account | Returns your account and business details including current plan. |
Reference: Appointments
GET /api/v1/appointments
| Parameter | Type | Description | |
|---|---|---|---|
| status | string | optional | confirmed, pending, cancelled |
| from | string | optional | Start date. Format: YYYY-MM-DD |
| to | string | optional | End date. Format: YYYY-MM-DD |
| phone | string | optional | Filter by client phone |
| limit | number | optional | Default: 100. Max: 500. |
GET /api/v1/appointments/:id
Returns a single appointment by ID.
POST /api/v1/appointments
| Field | Type | Description | |
|---|---|---|---|
| clientName | string | required | Full name of the client |
| phone | string | required | E.164 format (e.g. +15551234567) |
| date | string | required | Format: YYYY-MM-DD |
| time | string | optional | 24-hour format. Default: 09:00 |
| service | string | optional | Service name |
| notes | string | optional | Internal notes |
| status | string | optional | Default: confirmed |
PUT /api/v1/appointments/:id
Update any fields on an existing appointment. Only include what you want to change.
DELETE /api/v1/appointments/:id
Permanently deletes an appointment.
Reference: Clients
GET /api/v1/clients
| Parameter | Type | Description | |
|---|---|---|---|
| search | string | optional | Search by name, phone, or email |
| tag | string | optional | Filter by tag (exact match) |
| limit | number | optional | Default: 100. Max: 500. |
GET /api/v1/clients/:phone
Returns a single client by phone. URL-encode + as %2B.
POST /api/v1/clients
| Field | Type | Description | |
|---|---|---|---|
| name | string | required | Client's full name or company name |
| phone | string | optional | E.164 format |
| string | optional | Email address | |
| tags | array | optional | E.g. ["Commercial","VIP"] |
| address | string | optional | Street address |
| notes | string | optional | A note to add to the client's record |
PUT /api/v1/clients/:phone
Allowed fields: name, email, address, tags. Tags are merged, not replaced.
Reference: Inventory
GET /api/v1/inventory
| Parameter | Type | Description | |
|---|---|---|---|
| category | string | optional | Filter by category name |
| low_stock | boolean | optional | true returns only items at or below threshold |
POST /api/v1/inventory/:id/adjust
| Field | Type | Description | |
|---|---|---|---|
| type | string | required | add, remove, or set |
| qty | number | required | Quantity to add, remove, or set to |
| note | string | optional | Reason (stored in stock history) |
Reference: Quotes
GET /api/v1/quotes
| Parameter | Type | Description | |
|---|---|---|---|
| status | string | optional | draft, sent, approved, changes_requested |
| limit | number | optional | Default: 50. Max: 200. |
GET /api/v1/quotes/:id
Returns the full quote including line items, tax rates, and totals.
Reference: Invoices
GET /api/v1/invoices
| Parameter | Type | Description | |
|---|---|---|---|
| status | string | optional | outstanding or paid. Note: there's no separate "overdue" status — an outstanding invoice is overdue once dueDate has passed. |
| limit | number | optional | Default: 50. Max: 200. |
{
"count": 1,
"data": [
{
"id": "a1b2c3d4-...",
"invoiceNumber": 1042,
"clientName": "Jane Doe",
"clientPhone": "+1 555 000 0000",
"clientEmail": "[email protected]",
"items": [{ "description": "Cleaning", "qty": 1, "price": 100 }],
"amount": 100,
"dueDate": "2026-08-01",
"status": "outstanding",
"paidAt": null,
"appointmentId": null,
"signatureUrl": null,
"viewToken": "9f8e7d...",
"createdAt": "2026-07-01T12:00:00.000Z"
}
]
}
viewToken powers the client-facing, no-login invoice link at https://appt-guard.nihnyx.com/invoice/?token=<viewToken> — the same link sent automatically by email/text. If the connected business has Stripe Connect set up (Growth+ plan) and the invoice is unpaid, that page shows a live "Pay Now" button; otherwise it shows the business's contact info instead.
Reference: Reports
GET /api/v1/reports/appointments/stats
Returns appointment counts by status and for the current calendar month.
{
"total": 142,
"thisMonth": 18,
"confirmed": 130,
"cancelled": 9,
"pending": 3
}
GET /api/v1/reports/weekly-products
Breakdown of products and services booked in a date range. Requires Growth plan or above.
| Parameter | Type | Description | |
|---|---|---|---|
| from | string | optional | Start date. Default: 7 days ago. |
| to | string | optional | End date. Default: today. |
★ Reference: Expenses Elite
https://appt-guard.nihnyx.com/api/elite/expenses| Method | Endpoint | Description |
|---|---|---|
| GET | /api/elite/expenses | List all expenses, newest first. |
| POST | /api/elite/expenses | Create a new expense entry. |
| PUT | /api/elite/expenses/:id | Update an existing expense. |
| DEL | /api/elite/expenses/:id | Delete an expense. |
POST / PUT fields
| Field | Type | Description | |
|---|---|---|---|
| description | string | required | What the expense was for |
| amount | number | required | Dollar amount (e.g. 48.50) |
| date | string | required | Format: YYYY-MM-DD |
| category | string | optional | IRS Schedule C category. See full list above. Default: Other |
| notes | string | optional | Additional detail |
| receiptUrl | string | optional | URL to a receipt image or PDF |
★ Reference: Mileage Log Elite
https://appt-guard.nihnyx.com/api/elite/mileage| Method | Endpoint | Description |
|---|---|---|
| GET | /api/elite/mileage | List all trips, newest first. |
| POST | /api/elite/mileage | Log a new trip. |
| PUT | /api/elite/mileage/:id | Update a trip entry. |
| DEL | /api/elite/mileage/:id | Delete a trip entry. |
POST / PUT fields
| Field | Type | Description | |
|---|---|---|---|
| date | string | required | Format: YYYY-MM-DD |
| startAddress | string | required | Full starting address |
| endAddress | string | required | Full ending address |
| startTime | string | optional | Departure time. Format: HH:MM |
| endTime | string | optional | Arrival time. Format: HH:MM |
| miles | number | optional | Distance in miles. Can be added later via PUT. |
| purpose | string | optional | Reason for the trip (e.g. "Client visit") |
| notes | string | optional | Additional notes |
★ Reference: P&L Summary Elite
GET /api/elite/pnl
Returns a profit and loss summary for a date range. Revenue is sourced from paid invoices. Expenses come from your logged expense records.
| Parameter | Type | Description | |
|---|---|---|---|
| from | string | optional | Start date. Format: YYYY-MM-DD. Default: first day of current month. |
| to | string | optional | End date. Format: YYYY-MM-DD. Default: today. |
| Field | Type | Description |
|---|---|---|
| revenue | number | Total from paid invoices in the period |
| expenses | number | Total logged expenses in the period |
| netProfit | number | revenue minus expenses |
| invoiceCount | number | Number of paid invoices counted |
| expenseCount | number | Number of expense entries counted |
| revenueByClient | array | Each entry: { name, amount, pct } — sorted largest first |
| revenueByService | array | Each entry: { name, amount, pct } — sorted largest first |
| expensesByCategory | array | Each entry: { name, amount, pct } — sorted largest first |
★ Reference: Offline Pack Elite
GET /api/elite/offline-pack
Returns all appointments, form templates, and existing form submissions for a given date, packaged as a single JSON object. Use this to download the day's workload before entering a location without cell service.
| Parameter | Type | Description | |
|---|---|---|---|
| date | string | optional | Format: YYYY-MM-DD. Default: today. |
| Field | Type | Description |
|---|---|---|
| exportedAt | string | ISO timestamp when the pack was generated |
| date | string | The date this pack covers |
| templates | array | All your active form templates |
| appointments | array | All appointments scheduled for that date |
| submissions | array | Any form submissions already collected for those appointments |
Security Model
- Account-scoped. A key can only read and write data belonging to the account that generated it.
- Plan-enforced. Elite endpoints check your active subscription on every request. Downgrading your plan immediately blocks access to those endpoints.
- No administrative access. Keys cannot change billing, passwords, or platform settings.
- Audit trail. Every request is logged. View recent activity under Settings → API.
- Revocable instantly. Revoking a key in Settings takes effect within seconds.
Best Practices
- Use environment variables. Store your key in a
.envfile or your hosting platform's secret manager — never in your source code. - One key per application. Three tools, three keys. If one is compromised, revoke only that one.
- Never expose keys in client-side JavaScript. API calls must be made from your server.
- Check the plan field on /ping. Before making Elite API calls in your code, confirm
plan === "elite"so your app can give a clear message if the account has been downgraded. - Monitor the audit log. Check API activity in Settings periodically. Unexpected requests mean the key should be revoked immediately.
- Handle errors gracefully. Always check the HTTP status code. Handle 401, 403, and 429 explicitly so your application degrades cleanly.