Developer API
n8n integration ยท REST ยท Webhooks
OpenAPI JSONโ† Dashboard
Overview

The Glasshound REST API lets you automate bookings, check availability, and receive real-time webhook events. It integrates directly with n8n, Zapier, or any HTTP client.

Base URL
https://yoursalon.glasshound.app/api/v1
๐Ÿ”‘
API Key auth
Bearer tokens with per-scope control
๐Ÿ“…
Booking API
Availability, create, cancel, reschedule
๐Ÿช
Webhooks
HMAC-signed events pushed to your n8n
Authentication

All API requests require a Bearer token. Generate keys from Settings โ†’ API Keys in your dashboard.

curl -H "Authorization: Bearer gh_live_abc123..." \
  https://yoursalon.glasshound.app/api/v1/me

// Response
{ "tenantId": "ten_...", "salonName": "Nina's Pet Salon", "scopes": ["appointments:read", "appointments:write"] }

Available scopes: appointments:read appointments:write clients:read clients:write animals:read animals:write webhooks:read webhooks:write

Availability
GET/api/v1/availability

Returns open time slots for a service on a given date. Always call this before booking.

GET /api/v1/availability?serviceId=svc_abc&date=2025-06-15

{
  "data": {
    "date": "2025-06-15",
    "serviceId": "svc_abc",
    "slots": [
      { "startsAt": "2025-06-15T09:00:00Z", "endsAt": "2025-06-15T10:30:00Z" },
      { "startsAt": "2025-06-15T11:00:00Z", "endsAt": "2025-06-15T12:30:00Z" },
      { "startsAt": "2025-06-15T14:00:00Z", "endsAt": "2025-06-15T15:30:00Z" }
    ]
  }
}
Appointments
POST/api/v1/appointments
POST /api/v1/appointments
Authorization: Bearer gh_live_...

{
  "serviceId": "svc_abc123",
  "animalId": "ani_xyz789",
  "clientId": "cli_def456",
  "startsAt": "2025-06-15T09:00:00Z"
}

// 201 Response
{
  "data": {
    "id": "apt_...", "status": "CONFIRMED",
    "startsAt": "2025-06-15T09:00:00Z",
    "endsAt": "2025-06-15T10:30:00Z",
    "priceCents": 7500
  }
}
POST/api/v1/appointments/{id}/cancel
POST /api/v1/appointments/apt_abc/cancel
{ "reason": "Client requested cancellation" }

// Response
{ "data": { "id": "apt_abc", "status": "CANCELLED" } }
POST/api/v1/appointments/{id}/reschedule
POST /api/v1/appointments/apt_abc/reschedule
{ "newStartsAt": "2025-06-20T10:00:00Z", "reason": "Client requested new time" }

// Response
{ "data": { "id": "apt_abc", "status": "CONFIRMED", "startsAt": "2025-06-20T10:00:00Z" } }
PATCH/api/v1/appointments/{id}/status
PATCH /api/v1/appointments/apt_abc/status
{ "status": "CHECKED_IN" }

// Flow: CONFIRMED โ†’ CHECKED_IN โ†’ IN_PROGRESS โ†’ READY โ†’ COMPLETED
Clients
GET/api/v1/clients
GET /api/v1/clients?q=nina&page=1&pageSize=20

{
  "data": [{ "id": "cli_...", "name": "Nina Reyes", "email": "nina@example.com", "tier": "VIP" }],
  "meta": { "page": 1, "pageSize": 20, "total": 42, "pages": 3 }
}
POST/api/v1/clients
POST /api/v1/clients
{ "name": "Jane Smith", "email": "jane@example.com", "phone": "555-0100", "tier": "New" }
PATCH/api/v1/clients/{id}
PATCH /api/v1/clients/cli_abc
{ "tier": "VIP", "notes": "Prefers morning appointments." }
Animals
GET/api/v1/animals
GET /api/v1/animals?clientId=cli_abc&species=dog

{
  "data": [{
    "id": "ani_...", "name": "Biscuit", "species": "dog",
    "breed": "Golden Retriever", "weightLbs": "34.5",
    "allergies": ["lavender"], "behaviorFlags": ["anxious"],
    "preferredCadenceDays": 42, "lastVisitAt": "2025-04-30T00:00:00Z"
  }]
}
POST/api/v1/animals
POST /api/v1/animals
{
  "clientId": "cli_abc",
  "name": "Biscuit",
  "species": "dog",
  "breed": "Golden Retriever",
  "allergies": ["lavender"],
  "preferredCadenceDays": 42
}
Services
GET/api/v1/services
GET /api/v1/services?species=dog

{
  "data": [
    { "id": "svc_abc", "name": "Full Groom", "durationMinutes": 90, "priceCents": 7500, "species": "Dog" },
    { "id": "svc_def", "name": "Bath & Brush", "durationMinutes": 60, "priceCents": 5500, "species": null }
  ]
}
Grooming Notes
POST/api/v1/notes
POST /api/v1/notes
{
  "animalId": "ani_abc",
  "appointmentId": "apt_xyz",
  "tag": "behavior",
  "body": "Biscuit was anxious near clippers. Used slow approach. Completed successfully.",
  "visibility": "INTERNAL"
}

// Tags: general ยท allergy ยท behavior ยท grooming ยท medical ยท followup
Webhooks

Receive real-time push notifications when appointments change. Payloads are signed with HMAC-SHA256.

POST/api/v1/webhook-endpoints
POST /api/v1/webhook-endpoints
{
  "url": "https://your-n8n.example.com/webhook/glasshound",
  "events": ["appointment.created", "appointment.completed", "rebooking.due"],
  "description": "n8n booking automation"
}

// Response โ€” save signingSecret, shown only once!
{
  "data": { "id": "whe_...", "url": "...", "events": [...] },
  "signingSecret": "whsec_abc123...",
  "warning": "Save this signing secret โ€” it will not be shown again."
}
Verify signatures in n8n
// In a Code node before processing the webhook:
const crypto = require("crypto");
const ts = $input.first().headers["x-glasshound-timestamp"];
const sig = $input.first().headers["x-glasshound-signature"];
const body = JSON.stringify($input.first().body);

const expected = "sha256=" + crypto
  .createHmac("sha256", $env.GLASSHOUND_WEBHOOK_SECRET)
  .update(`${ts}.${body}`)
  .digest("hex");

if (sig !== expected) throw new Error("Invalid webhook signature");
return $input.all();
Event payload shapes
// appointment.created / appointment.completed / appointment.cancelled
{
  "event": "appointment.created",
  "tenantId": "ten_...",
  "timestamp": "2025-06-15T09:00:00Z",
  "data": {
    "appointmentId": "apt_...", "animalId": "ani_...",
    "clientId": "cli_...", "serviceId": "svc_...",
    "startsAt": "2025-06-20T10:00:00Z", "status": "CONFIRMED"
  }
}

// rebooking.due
{
  "event": "rebooking.due",
  "tenantId": "ten_...",
  "data": {
    "animalId": "ani_...", "animalName": "Biscuit",
    "clientId": "cli_...", "clientName": "Jane Smith",
    "clientEmail": "jane@example.com",
    "daysSinceLastVisit": 45, "preferredCadenceDays": 42
  }
}
n8n Integration Guide
Full booking automation in 4 nodes

Webhook trigger โ†’ HTTP Request (check availability) โ†’ Set node (pick slot) โ†’ HTTP Request (create booking). No code required โ€” just configure the HTTP Request nodes with your API key.

Workflow: New booking from form
// 1. Webhook trigger receives form data:
{ "petName": "Biscuit", "clientEmail": "jane@example.com", "serviceId": "svc_abc", "preferredDate": "2025-06-20" }

// 2. HTTP Request โ€” GET /api/v1/availability
//    URL: https://yoursalon.glasshound.app/api/v1/availability?serviceId={{ $json.serviceId }}&date={{ $json.preferredDate }}
//    Auth: Header Auth โ†’ Authorization: Bearer {{ $env.GLASSHOUND_KEY }}

// 3. Set node โ€” pick first slot
//    startsAt: {{ $json.data.slots[0].startsAt }}

// 4. HTTP Request โ€” POST /api/v1/appointments
{
  "serviceId": "{{ $('Webhook').item.json.serviceId }}",
  "animalId": "{{ $('Lookup Animal').item.json.data[0].id }}",
  "clientId": "{{ $('Lookup Client').item.json.data[0].id }}",
  "startsAt": "{{ $('Pick Slot').item.json.startsAt }}"
}
Workflow: Rebooking reminders
// 1. Glasshound fires "rebooking.due" webhook to your n8n URL
// 2. Webhook trigger node receives it
// 3. Code node verifies the HMAC signature (see above)
// 4. Send Email node โ†’ "Time to book {{ $json.data.animalName }}'s next groom!"
// 5. Optional: HTTP Request โ†’ POST /api/v1/appointments (auto-book next open slot)
Quick-start steps
  1. In n8n, create a Header Auth credential โ†’ name: Authorization, value: Bearer gh_live_โ€ฆ
  2. Call POST /api/v1/webhook-endpoints with your n8n webhook URL
  3. Save the signingSecret as an n8n env var: GLASSHOUND_WEBHOOK_SECRET
  4. Build your workflows using the HTTP Request node with the credential above
  5. Activate your n8n workflow โ€” bookings flow automatically!
Error Reference
HTTP StatusMeaningCommon cause
401UnauthorizedMissing or invalid API key
403ForbiddenKey lacks required scope for this operation
404Not foundResource doesn't exist in your tenant
409ConflictSlot is no longer available
422Validation errorRequest body failed schema validation
429Rate limitedSlow down โ€” retry after Retry-After header
503Service unavailableDatabase connection failed
// All errors share this envelope:
{
  "error": "Validation error",
  "status": 422,
  "details": {
    "fieldErrors": { "startsAt": ["Required"] },
    "formErrors": []
  }
}