Overview
SepticCycle exposes a webhook system that fires real-time events when key actions occur in your account — jobs completed, invoices paid, contracts signed, and more. These events let you push data to external systems the moment it happens, without polling.
A full REST API with read/write access to customers, jobs, invoices, tanks, and compliance records is in active development. Request early access below to get notified when it launches and to influence which endpoints ship first.
Webhooks
LiveREST API
Coming SoonAPI Keys
In SettingsAuthentication
API key generation is not yet available as a self-serve feature in the dashboard. To get your API credentials, request early access using the form at the bottom of this page — we'll issue your key directly and walk you through the setup.
📌 Note: Self-serve API key management (generate, rotate, revoke) is on the roadmap and will be added to Settings when the full REST API launches. Until then, all API access is provisioned manually — reach out via the contact form below.
Using Your Key
Once your API key has been provisioned, include it in the Authorization header on every request:
Authorization: Bearer YOUR_API_KEY📌 Note: API keys are scoped to your company. One key per company — rotate it from Settings at any time. Never commit your API key to source control.
Webhooks
Webhooks deliver real-time POST requests to an endpoint URL you configure whenever a supported event occurs in SepticCycle. Your endpoint receives a JSON payload and must respond with HTTP 200 within 10 seconds.
Setting Up a Webhook
Webhook endpoint configuration is provisioned manually during early access. When you request API access below, include your endpoint URL and the events you want to subscribe to — we'll configure it on our end and confirm when it's active.
📌 Note: Self-serve webhook management (add endpoint, select events, view delivery logs) is on the roadmap alongside the full REST API. Until then, all webhook setup is handled directly by the SepticCycle team.
Retry Logic
If your endpoint does not return HTTP 200, SepticCycle retries the delivery three times with exponential backoff — at 1 minute, 5 minutes, and 30 minutes. After three failed attempts, the event is marked as failed. During early access, contact us if you need to replay a failed event.
Webhook Events
The following events are currently supported:
| Event | When It Fires |
|---|---|
| job.completed | Admin or technician marks a job as Completed |
| invoice.paid | Stripe payment succeeds or offline payment is recorded |
| contract.signed | Both customer and company signatures are captured |
Sample Payload — job.completed
{
"event": "job.completed",
"job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"company_id": "9988ed11-170d-430d-b28a-4a41a153e105",
"customer_id": "uuid",
"property_id": "uuid",
"primary_technician_id": "uuid",
"technician_ids": ["uuid", "uuid"],
"service_type": "Aerobic Inspection",
"completed_at": "2026-03-20T14:30:00Z",
"invoice_id": "uuid"
}Sample Payload — invoice.paid
{
"event": "invoice.paid",
"invoice_id": "uuid",
"company_id": "uuid",
"customer_id": "uuid",
"amount": 29900,
"currency": "usd",
"payment_method": "card",
"paid_at": "2026-03-20T15:00:00Z"
}Sample Payload — contract.signed
{
"event": "contract.signed",
"contract_id": "uuid",
"company_id": "uuid",
"customer_id": "uuid",
"customer_signed_at": "2026-03-20T10:00:00Z",
"company_signed_at": "2026-03-20T11:30:00Z"
}Signature Verification
Every webhook request includes an X-SepticCycle-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret. Always verify this signature before processing the event.
import crypto from 'crypto';
function verifyWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
// In your Express route:
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-septiccycle-signature'];
if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Handle event...
res.sendStatus(200);
});REST API — Coming Soon
📌 Note: The REST API is in active development. The endpoints below are planned — they are not yet available. Request early access to be notified when they launch and to provide input on which endpoints ship first.
The REST API will provide read/write access to all core SepticCycle data. Planned endpoints:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/jobs | Create a new job |
| GET | /api/v1/customers | List or search customers |
| GET | /api/v1/tanks/{id} | Get tank with full service history |
| POST | /api/v1/reports/compliance | Generate a compliance report |
| GET | /api/v1/invoices | List invoices with filters |
| GET | /api/v1/contracts | List contracts by status |
💡 Tip: The endpoints you care most about influence what ships first. Tell us your use case in the early access form below — QuickBooks sync, Zapier automation, custom dashboards, and county reporting integrations are the most common requests.
Code Examples
cURL — Test Your Webhook Endpoint
curl -X POST https://your-server.com/webhook \
-H "Content-Type: application/json" \
-H "X-SepticCycle-Signature: test_signature" \
-d '{
"event": "job.completed",
"job_id": "test-uuid",
"service_type": "Aerobic Inspection",
"completed_at": "2026-03-20T14:30:00Z"
}'Node.js — Handle job.completed and Notify Slack
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook/septiccycle', async (req, res) => {
// 1. Verify signature
const sig = req.headers['x-septiccycle-signature'];
const expected = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
return res.status(401).send('Unauthorized');
}
const event = JSON.parse(req.body);
// 2. Handle job.completed
if (event.event === 'job.completed') {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `✅ Job completed: ${event.service_type} — Invoice: ${event.invoice_id}`,
}),
});
}
res.sendStatus(200);
});Python — Handle invoice.paid
from flask import Flask, request, abort
import hmac, hashlib, json, os
app = Flask(__name__)
@app.route('/webhook/septiccycle', methods=['POST'])
def handle_webhook():
# Verify signature
sig = request.headers.get('X-SepticCycle-Signature', '')
secret = os.environ['WEBHOOK_SECRET'].encode()
expected = hmac.new(secret, request.data, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
abort(401)
event = json.loads(request.data)
if event['event'] == 'invoice.paid':
# Push to your accounting system
amount_dollars = event['amount'] / 100
print(f"Payment received: ${amount_dollars:.2f} — Invoice {event['invoice_id']}")
return '', 200Request Early API Access
Webhooks are live and available to all SepticCycle customers today. The full REST API is coming soon. Fill out the form below and we'll reach out directly with your API credentials and early access details.
Get notified when the REST API launches
We review every request personally. Tell us what you're building and we'll prioritize the endpoints that matter most to you.
When contacting us, select API / Webhook Integration in the "What do you need?" checkboxes and describe your use case in the message field.