API & Webhooks

SepticCycle API & Webhooks

Real-time integration for septic businesses. Connect SepticCycle to your accounting software, CRM, mapping tools, or custom dashboards. API access is included in every $149/mo plan — no add-ons required.

Webhooks — Live REST API — Coming SoonRequest Early Access

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

Live

REST API

Coming Soon

API Keys

In Settings

Authentication

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:

HTTP Header
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:

EventWhen It Fires
job.completedAdmin or technician marks a job as Completed
invoice.paidStripe payment succeeds or offline payment is recorded
contract.signedBoth customer and company signatures are captured

Sample Payload — job.completed

JSON
{
  "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

JSON
{
  "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

JSON
{
  "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.

Node.js — Verify Signature
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:

MethodEndpointDescription
POST/api/v1/jobsCreate a new job
GET/api/v1/customersList or search customers
GET/api/v1/tanks/{id}Get tank with full service history
POST/api/v1/reports/complianceGenerate a compliance report
GET/api/v1/invoicesList invoices with filters
GET/api/v1/contractsList 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
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

Node.js
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

Python
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 '', 200

Request 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.