INFO

Zapier Integration

A close up shot of the GMM CNC controller

StonePad Integration API

1. Authentication

StonePad uses OAuth 2.0 Authorization Code Grant with PKCE (RFC 6749 § 4.1 + RFC 7636). Bearer access tokens are required for every API call except the OAuth endpoints themselves.Two scopes are defined:

Scope Grants
leads:read
Reading leads
GET /api-leads GET /api-me
leads:write
Creating leads and uploading attachments
POST /api-leads POST /api-lead-attachments

Applications must be registered by StonePad support (support@stonepad.co.uk) to receive client_id, client_secret, and registered redirect_uri values. Client secrets are stored as SHA-256 hashes; the plain value is only shown once at registration.

1.1 Authorize endpoint

GET https://api.stonepad.app/functions/v1/oauth-authorize

Query parameters:

Parameter Required Description
response_type Yes Must be code
client_id Yes Your registered client_id
redirect_uri Yes Must exactly match a URI registered for the client
scope Yes Space or comma separated list of requested scopes
state Recommended Opaque value returned to the client for CSRF protection
code_challenge Yes PKCE code challenge (43–128 chars)
code_challenge_method Yes Must be S256

The user is redirected through StonePad's consent screen and, on approval, sent back to redirect_uri with ?code=<authorization_code>&state=<state>. Authorization codes are single-use and expire in 5 minutes.

Errors are returned as query-string parameters on redirect_uri (?error=<code>&error_description=<text>&state=<state>) per RFC 6749 § 4.1.2.1.

1.2 Token endpoint

POST https://api.stonepad.app/functions/v1/oauth-token
Content-Type: application/x-www-form-urlencoded

papplication/json bodies are also accepted. Client credentials may be sent in the body or via HTTP Basic authentication (RFC 6749 § 2.3.1).

Supported grant types:

Authorization code exchange:

Field Description
grant_type authorization_code
client_id Your registered client_id
client_secret Your registered client_secret
code Value received on redirect
code_verifier PKCE verifier that hashes to the challenge
redirect_uri Same value used in the authorize call

Refresh token rotation (RFC 6749 § 10.4 – rotates on every use)

Field Description
grant_type refresh_token
client_id Your registered client_id
client_secret Your registered client_secret
refresh_token Current refresh token

Success response:

{
  "access_token": "…",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "leads:read leads:write"
}
  • Access tokens last 1 hour (3600 seconds).
  • Refresh tokens last 30 days and are rotated on every use — the previous refresh token is revoked as soon as a new pair is issued.

Errors follow RFC 6749 § 5.2 (invalid_request, invalid_client, invalid_grant, unsupported_grant_type, invalid_scope, etc.):

{ "error": "invalid_grant", "error_description": "Authorization code expired" }

1.3 Revoke endpoint

POST https://api.stonepad.app/functions/v1/oauth-revoke
Content-Type: application/x-www-form-urlencoded

RFC 7009 revocation. Fields:

Field Description
client_id Your registered client_id
client_secret Your registered client_secret
token The access or refresh token to revoke
token_type_hint Optional: access_token or refresh_token

1.4 Making authenticated requests

Every API call must include:

Authorization: Bearer <access_token>

401 responses indicate the token is missing, invalid, expired, or revoked. 403 responses indicate the token is valid but lacks the required scope.

2. GET /api-me — Verify credentials

Returns the identity behind the current access token. Zapier calls this immediately after the OAuth flow to prove the connection works, and uses the response to build the human-readable connection label.

GET /api-me
Authorization: Bearer <access_token>

Required scope: any (no scope check).

Response 200 OK:

{
  "id": "e7c1f0d3-…-…",
  "email": "user@example.com",
  "name": "Jane Doe",
  "company_id": "c9a…",
  "company_name": "Example Kitchens Ltd",
  "membership_role": "owner",
  "scopes": ["leads:read", "leads:write"]
}

3. POST /api-leads — Create Lead

Creates a lead on the authenticated user's active company. Powers Zapier's "Create Lead in StonePad" action.

Required scope: leads:write.

POST /api-leads
Authorization: Bearer <access_token>
Content-Type: application/json

3.1 Request body

All fields are strings unless otherwise noted. name or email is required; the rest are optional.

Field Type Max length Notes
name string 200 Required if email is absent
email string 200 Required if name is absent
phone string 50
company string 200
source string 100 Defaults to Zapier when omitted
message string 5000 Free-text description of the enquiry
budget string 100 Currency-neutral, stored verbatim
status string 100 Base values: new, cold, hot, converted. Custom statuses configured per-company are also accepted (by value or label, case-insensitive). Defaults to new.
priority string One of High, Medium, Low (case-insensitive). Defaults to Medium.
due_date string ISO 8601 date (YYYY-MM-DD or full ISO datetime). Stored as date.
handed_to string 200 Staff name — assigns the lead and triggers an assignment email.
address_line1 string 200
address_line2 string 200
town string 100
county string 100
post_code string 20
custom_fields object 32 KiB serialized Flat { "Question label": "Answer" } map for source-specific enquiry fields. Max 50 keys. Keys up to 120 chars, values up to 2000 chars. Numbers and booleans are auto-stringified.
attachments string[] or string 20 entries, 2048 chars per URL Absolute http(s) URLs. Accepts a JSON array or a single string split on whitespace/commas. Duplicates are removed. Typeform's authenticated file URL (https://api.typeform.com/forms/…/files/…) is explicitly rejected — use Typeform's public signed URL variant instead.
{
  "name": "Carl Smith",
  "email": "carl@example.com",
  "phone": "07306100000",
  "company": "Kitchen Company",
  "source": "Website",
  "message": "Quote requested for new granite worktop.",
  "budget": "5000",
  "status": "new",
  "priority": "High",
  "due_date": "2026-10-01",
  "handed_to": "Jeoff Jones",
  "post_code": "SW1A 1AA",
  "custom_fields": {
    "Finish": "Polished",
    "Thickness": "20mm"
  },
  "attachments": [
    "https://api.typeform.com/responses/files/<hash>/kitchen.pdf",
    "https://api.typeform.com/responses/files/<hash>/measurements.jpg"
  ]
}

Response 201 Created returns the full stored lead row, including generated id, timestamps, tenant scope (company_id, branch_id), and normalized field values:

{
  "id": "1fc1a338-fd66-414c-8924-efb69888000a",
  "name": "Carl Smith",
  "email": "carl@example.com",
  "phone": "07306100000",
  "company": "Kitchen Company",
  "source": "Website",
  "status": "new",
  "priority": "High",
  "due_date": "2026-10-01",
  "handed_to": "Jeoff Jones",
  "custom_fields": { "Finish": "Polished", "Thickness": "20mm" },
  "attachments": ["https://…/kitchen.pdf", "https://…/measurements.jpg"],
  "created_by": "Zapier",
  "created_at": "2026-09-15T14:20:41.650Z",
  "updated_at": "2026-09-15T14:20:41.650Z"
}

3.2 Validation errors (all 400 Bad Request)

  • Either name or email is required
  • Status "<value>" is not one of the allowed values. Valid options: <list>.
  • custom_fields must be a flat object of key -> value pairs
  • custom_fields may contain at most 50 keys
  • attachments must be absolute http(s) URLs; received "<snippet>"
  • attachments may not exceed 20 entries
  • Typeform's authenticated file URL is not usable as an attachment.

422 Unprocessable Entity is returned when the authenticated company has no branches configured (a StonePad-side prerequisite before leads can be created).

4. GET /api-leads — List recent leads

Polling endpoint that returns leads most-recent-first, optionally filtered by creation timestamp. Powers Zapier's "New Lead in StonePad" polling trigger.

GET /api-leads?since=<iso8601>&limit=<n>
Authorization: Bearer <access_token>

Required scope: leads:read.

Query param Required Description
since No ISO 8601 timestamp. Only leads with created_at strictly greater than this value are returned. If omitted, the most recent leads (up to limit) are returned.
limit No Max results per response. Default 100. Minimum 1, maximum 200.

Response 200 OK: array of full lead objects (same shape as the POST response), ordered by created_at DESC.

Zapier's recommended polling pattern is to store the highest created_at seen and pass it as since on the next call

5. POST /api-lead-attachments — Upload lead attachment

Uploads a file into the tenant's lead-attachments Storage bucket and returns a stable public URL. Intended for source forms whose file URLs are behind authentication (Typeform, HubSpot, JotForm private forms) and cannot be linked to directly from a lead detail page.

POST /api-lead-attachments
Authorization: Bearer <access_token>

Required scope: leads:read.

Two request shapes are supported. Zapier normally uses the JSON form because Zapier's own String-typed inputs cannot reliably send multipart:

5.1 Multipart upload (raw bytes)

Content-Type: multipart/form-data; boundary=…
  • Form field file: the file bytes (required, must be non-empty).
  • Maximum size: 25 MiB per request.

5.2 URL fetch (server-side download)

Content-Type: application/json

{ "url": "https://example.com/path/to/file.pdf" }
  • Only https:// URLs are accepted.
  • The response is fetched with follow-redirects. Filename is derived from the response Content-Disposition header, falling back to the URL path, then to "download".
  • Maximum size: 25 MiB (checked against Content-Length first, then against actual bytes read).

5.3 Response

201 Created:

{
  "url": "https://<supabase-project>.supabase.co/storage/v1/object/public/lead-attachments/<company_id>/<yyyy-mm>/<uuid>-<filename>",
  "path": "<company_id>/<yyyy-mm>/<uuid>-<filename>",
  "filename": "kitchen.pdf",
  "size": 184320,
  "content_type": "application/pdf"
}

The returned url is stable and publicly readable. Pass it into the attachments array of a subsequent POST /api-leads call to attach it to a lead.

Error responses:

Status Meaning
400 Invalid body, unsupported Content-Type, non-HTTPS URL, empty file, or fetch failure
401 Missing or invalid access token
403 Token lacks leads:write
413 File exceeds 25 MiB
500 Storage upload failed

6. Error format

All non-2xx responses (except for OAuth errors on redirect URIs, which follow RFC 6749) use a single-key JSON body

{ "error": "Human-readable message describing the failure." }

OAuth token/revoke endpoints additionally return a machine-readable code

{ "error": "invalid_grant", "error_description": "Authorization code expired" }

CORS is enabled for all endpoints (Access-Control-Allow-Origin: *) so browser-based integrations can call them directly.

7. Rate limits and quotas

  • Access token TTL: 1 hour. Refresh before expiry using the refresh token grant.
  • Refresh token TTL: 30 days from initial issuance. Rotated on every use.
  • Authorization code TTL: 5 minutes, single-use.
  • Attachment size: 25 MiB per file.
  • Attachment count: 20 URLs per lead.
  • Custom fields: 50 keys, 32 KiB serialized total, 120 chars per key, 2000 chars per value.
  • List page size: 200 records per response.

There is no hard per-minute rate limit at the API layer today. Traffic that materially exceeds normal polling patterns (one poll every 1–5 minutes per connection) will be reviewed and may be throttled at the platform layer. Please contact support before running batch backfills so we can whitelist your source IP.

8. Support and account management

  • Application registration and credential rotation: support@stonepad.co.uk
  • Endpoint status / incident notifications: status@stonepad.co.uk
  • Response time: business hours (Monday–Friday, 09:00–17:30 UK).

Include your client_id (never the client_secret) and, where applicable, an example request/response pair when reporting integration issues.

9. Versioning and change policy

This is v1 of the Integration API. Backwards-incompatible changes will be published under a new base path (e.g. /v2/) with at least 90 days of parallel availability. Additive changes — new optional request fields, new response fields, new endpoints, new scopes — may ship at any time and will be announced in advance to registered application owners at the address on record.

Stay compliant with our all-in-one health & safety management system

Make your business compliant.

Start Today