Integrations API

Can Cleaning Software

Last updated: October 27, 2026 · Operated by Josh McCauley LLC

On this page
  1. Overview
  2. Authentication
  3. Permission scopes
  4. Rate limits
  5. Endpoints
  6. Pagination and list filters
  7. Durable change feeds
  8. Recent changes (Zapier-style polling)
  9. Customer lookup
  10. Error responses
  11. Zapier integration
  12. Security and data handling

Overview

Can Cleaning Software provides a secure, tenant-scoped, read-only HTTP API for Google Sheets, Zapier, and other third-party tools.

This page documents the production Integrations API v1. All endpoints below are GET-only and never mutate CRM data. Write APIs, REST-hook webhooks, and administrative key-management HTTP endpoints are not part of this public surface.

Production base URL

All requests use https://api.cancleaningsoftware.com. Paths are under /api/v1/integrations. Do not send credentials to any other host.

  • Authentication is by tenant API key only (not staff JWT sessions)
  • Tenant identity always comes from the authenticated key — caller-supplied tenant ids are ignored for authorization
  • Rate limit: 120 requests per minute per API key
  • Card numbers, payment tokens, private notes, and other secrets are never returned

Authentication

Create an API key

  • Sign in to Can Cleaning Software as an administrator
  • Open Settings → Integrations → API Access
  • Enable external API access if it is disabled
  • Create a key and select only the scopes your integration needs
  • Copy the secret once when it is shown (it starts with ccs_ak_…). Store it in your integration’s secure credential store — never in spreadsheets, chat, or source control

Send the key on every request

Use either header:

X-Api-Key: ccs_ak_…

or:

Authorization: Bearer ccs_ak_…

Staff JWT sessions cannot call Integrations API endpoints. Integration API keys cannot call JWT-only admin endpoints.

Verify a key

GET /api/v1/integrations/me HTTP/1.1
Host: api.cancleaningsoftware.com
X-Api-Key: ccs_ak_…

Permission scopes

Keys are limited to the scopes granted at creation time. Missing a required scope returns HTTP 403.

ScopeAccess
customers:read/customers, /customers/{id}, /customers/lookup, /customers/changes, /customers/changes/recent
services:read/services, /services/changes
jobs:read/jobs, /jobs/changes
routes:read/routes, /routes/changes

GET /me succeeds with any valid key and returns the key’s tenant, name, scopes, and optional expiry.

Rate limits

Integrations API traffic is limited to 120 requests per minute per API key (policy name rl-integration-api). When the limit is exceeded the API returns HTTP 429 with a Retry-After header.

  • Partitioning is by authenticated API key id when present
  • Failed or pre-auth requests are partitioned separately so one caller cannot exhaust another key’s budget
  • Design connectors to poll change feeds rather than hammering list endpoints

Endpoints

Base path: https://api.cancleaningsoftware.com/api/v1/integrations

MethodPathRequired scope
GET/meany valid key
GET/customerscustomers:read
GET/customers/lookupcustomers:read
GET/customers/{id}customers:read
GET/customers/changescustomers:read
GET/customers/changes/recentcustomers:read
GET/servicesservices:read
GET/services/changesservices:read
GET/jobsjobs:read
GET/jobs/changesjobs:read
GET/routesroutes:read
GET/routes/changesroutes:read

Read-only

These routes never create, update, or delete CRM records. There are no public write endpoints in Integrations API v1.

Pagination and list filters

List endpoints accept page (1-based) and pageSize (default 50, maximum 100). Prefer keyset pagination with afterId for snapshots — offset page numbers are convenience-only and are not sync-safe under concurrent inserts or deletes.

Keyset snapshot

GET /api/v1/integrations/customers?pageSize=100
GET /api/v1/integrations/customers?pageSize=100&afterId={nextAfterId}

Responses include items, page, pageSize, totalCount, hasMore, and nextAfterId (null when finished). Optional filters vary by resource (for example customer status, routeId, updatedSinceUtc; service customerId / activeOnly; job date range / status; route activeOnly).

Customer list and detail responses include scheduling and billing-health fields such as storedNextServiceDate, actualNextServiceDate, paymentStatus, and billingAlert. Ledger and payment-health fields are null when an authoritative calculation fails (they are never silently inventing a “current” balance). Card data and private notes are never exposed.

Durable change feeds

Each resource type (customer, service, job, route) has a durable, ordered change stream. Poll /{resource}/changes with an opaque checkpoint cursor for lossless catch-up.

GET /api/v1/integrations/customers/changes?limit=100
GET /api/v1/integrations/customers/changes?cursor={checkpoint}&limit=100

Every response includes checkpoint (always set, including empty pages), hasMore, streamHead, and items. Default limit is 100 (maximum 200). Persist checkpoint only after you successfully process the page. On failure, retry the same cursor (at-least-once delivery; upsert by resourceId).

changeKindRecommended action
createdInsert or upsert by resourceId
upsertedUpsert by resourceId
deactivatedUpsert and mark inactive / archived
deletedRemove the row (data is null)

Each item’s changeKind is the historical event recorded at commit time. data plus currentStatus / currentIsActive / resourceExists / polledAtUtc describe the live resource at poll time and may differ after later transitions (for example deactivated, then reactivated).

Initial sync recipe

1) Capture streamHead (or the checkpoint at head) before snapshotting. 2) Snapshot with afterId keyset until hasMore is false. 3) Catch up /changes from that boundary. 4) Continue polling with the last saved checkpoint.

Recent changes (Zapier-style polling)

For stateless pollers that cannot share a durable cursor, use the newest-first recent feed:

GET /api/v1/integrations/customers/changes/recent?limit=100&kinds=created
GET /api/v1/integrations/customers/changes/recent?limit=100&kinds=upserted
GET /api/v1/integrations/customers/changes/recent?limit=100&kinds=deactivated

Optional kinds is a comma-separated list of created, upserted, deactivated, deleted. Deduplicate by changeId. Default limit is 100 (maximum 200). When truncated is true, more matching events exist beyond the window and may be missed between polls under high volume — use the durable /changes checkpoint feed (or a future webhook) when lossless sync is required.

Customer lookup

Bounded exact-match lookup for Find Customer style searches. Provide at least one of id, email, or phone. Results are capped (default 10, maximum 25). This is not unrestricted free-text search.

GET /api/v1/integrations/customers/lookup?email=ada@example.test
GET /api/v1/integrations/customers/lookup?phone=208-555-0100
GET /api/v1/integrations/customers/lookup?id={guid}

Error responses

HTTP statusMeaning
400Invalid request (for example lookup with no id/email/phone)
401Missing, invalid, revoked, or expired key, or external API access disabled
402Tenant billing suspended
403Authenticated key is missing the required scope
404Resource not found in this tenant (including cross-tenant ids)
429Rate limited — wait and retry (Retry-After: 60)

Error bodies are generic and do not echo secrets. Treat 401 uniformly — do not probe for key state.

Zapier integration

Can Cleaning Software publishes an official Zapier Platform CLI app (invite-only while in private release). Scope of the app: read-only customer triggers plus Find Customer. No writes, messaging, payments, or scheduling actions.

Connect a tenant account

  • In the CRM: Settings → Integrations → API Access → enable access and create a key with customers:read (name it e.g. Zapier)
  • Copy the secret once (ccs_ak_…)
  • In Zapier, connect Can Cleaning Software and paste the key into the secure API Key field
  • Connection test calls GET /api/v1/integrations/me and labels the connection with tenant and key name
  • The Zapier app always calls https://api.cancleaningsoftware.com — the API host is not user-configurable

Supported triggers

TriggerAPIDedup keyNotes
New CustomerGET /customers/changes/recent?kinds=createdchangeIdFires on CRM create only — not on later edits
Updated CustomerGET /customers/changes/recent?kinds=upsertedchangeIdEach update event stays distinct
Customer DeactivatedGET /customers/changes/recent?kinds=deactivatedchangeIdArchived/deactivated only

Supported search

  • Find Customer — exact match on Customer ID, email, and/or phone via GET /customers/lookup

Polling notes

  • Zapier remembers seen changeId values per Zap; there is no shared cursor across Zaps
  • changeKind / eventKind is historical; live status fields may differ after later transitions
  • Polling is not lossless under sustained high volume when truncated is true — prefer the Google Sheets checkpoint sync or a future webhook for full catch-up

Not included in Zapier (this release)

Create/update/delete actions, messaging, payments, scheduling, and REST-hook (instant) triggers are not available. Use the HTTP change-feed endpoints above for custom connectors that need durable sync.

Security and data handling

  • TLS to the production API host
  • Never put API keys in query strings, sample payloads, logs, or error messages
  • Revoke keys immediately from Settings → Integrations → API Access if a secret may have leaked
  • Responses never include full card numbers, payment tokens, or private internal notes
  • Each key is scoped to a single tenant; cross-tenant ids return 404

Questions: support@cancleaningsoftware.com

See also our Privacy Policy and Terms and Conditions.

CanCleaningSoftware · Integrations API · Privacy Policy · Delete Account · Terms and Conditions · support@cancleaningsoftware.com