For you and your web person

Connect CETECommand to your website.

The enquiry form, website chat and client portal, on your own site, in minutes. Every enquiry and chat lands in CETECommand, ready to quote.

Your key is in the app JobsSettingsBooking page Website code shows your code with your key, ready to copy.

Enquiry form

Quotes, maintenance requests, or both. Up to 6 photos. In your colours, and it fits any space.

<div data-cete="enquiry-form"></div>

Website chat

A chat button in the corner. You answer from the app. Gone before you reply? Your answer reaches them by email.

data-chat

Client portal

A Client login link. Customers sign in with their email to see their jobs, approve quotes and pay invoices.

<div data-cete="client-login"></div>

Before you start

  1. Open the booking page settings. In CETECommand: Jobs, Settings, Booking page. Turn the booking page on.
  2. List your website. Add its address under Your websites. Tick Website chat and Client portal if you want them. Save.
  3. Copy your code. Website code now shows the script with your key. Pick your platform below to see where it goes.
Step by step

Pick your platform.

Each one takes a few minutes. Replace YOUR_BOOKING_KEY with the key from Website code, or copy the code straight from the app.

WordPress

About 5 minutes

Use the CETECommand plugin. No code to paste, and it keeps working when you change themes.

  1. Download the plugin

    Download the plugin (.zip)

    Keep it as a .zip file. WordPress wants it that way.

  2. Upload it to WordPress

    In your WordPress dashboard: PluginsAdd New PluginUpload Plugin. Choose cetecommand.zip, press Install Now, then Activate.

  3. Add your booking key

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

    Then in WordPress go to SettingsCETECommand. Paste your booking key (the data-key from Website code) into Booking key. Tick Website chat for a chat button on every page, then press Save Changes. Leave CETECommand address as it is.

  4. Put the form on a page

    Edit your Contact page, add a Shortcode block and paste this:

    Enquiry form
    [cete_enquiry_form]
    Options
    [cete_enquiry_form type="work_order"]
    [cete_enquiry_form type="quote" service="Roofing" title="Get a quote"]
  5. Add a chat button and client login

    Optional. Use these anywhere: a page, a sidebar or the footer.

    Chat button and client login
    [cete_chat_button label="Chat with us"]
    [cete_client_login label="Client login"]
  6. Already have a contact form?

    Contact Form 7, WPForms and Elementor Pro forms can send their entries to CETECommand too. In SettingsCETECommand, paste an API key (in CETECommand: JobsSettingsAPI and webhooks, Make a key) into API key. Tick your form plugin under Send entries from, pick quote or maintenance requests under Send them as, and press Save Changes. Test the API key checks it works. The key stays on your server.

Rather not use a plugin? Paste the one-line script from Any other website into your theme footer (or a header and footer code plugin), and use a Custom HTML block for the form.

WooCommerce

About 10 minutes

The same CETECommand plugin, with a WooCommerce section for your shop.

  1. Install the plugin

    Download the plugin (.zip)

    PluginsAdd New PluginUpload Plugin, choose cetecommand.zip, Install Now, Activate.

  2. Add your booking key

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

    In WordPress: SettingsCETECommand. Paste your booking key into Booking key and press Save Changes.

  3. Turn on the shop extras

    On the same page, the WooCommerce section shows when WooCommerce is active. Everything in it is off until you tick it. Choose what you want, then Save Changes:

    • Chat on shop pages. The chat button on Product pages, the Cart and Checkout only. Customers ask before they buy, and you answer from the app. (Turn on Website chat in CETECommand too.)
    • Product questions. A "Questions about this product?" link under Add to cart that opens the chat. If chat is off, it opens your booking page.
    • Quote request button. On every product, or Only on products with no price. For things you price per job: it opens your enquiry form with the product filled in, and the request lands in Jobs, Enquiries.
    • My Account. A "Your jobs and invoices" link to your client portal. It shows while Client portal is on in CETECommand.
  4. Send orders into CETECommand

    In CETECommand: InvoicingSettingsShop orders. Press Turn on shop orders and copy the address (it is shown once).

    In WordPress, under WooCommerce orders into CETECommand on the same plugin page, paste it into Shop orders address and press Connect. The plugin adds the WooCommerce webhooks for you (Order created and Order updated) and sends a test. Each order then becomes an invoice in Invoicing, and paid orders are marked paid.

    Keep the address secret: anyone with it can make invoices. Shop orders are on paid plans and in your free months.

  5. Shortcodes for your own layout

    The shortcodes from the WordPress steps work here too: [cete_enquiry_form], [cete_chat_button] and [cete_client_login]. On a product page you can also place the quote button and questions link yourself:

    Product page shortcodes
    [cete_quote_button label="Ask for a quote"]
    [cete_product_question label="Questions about this product?"]

No plugin? Make the webhook yourself: WooCommerceSettingsAdvancedWebhooksAdd webhook. Status Active, Topic Order created, the shop orders address as the Delivery URL. Add a second one for Order updated to catch payments later. Use the plugin or your own webhooks, not both, so each order comes in once.

Shopify

About 10 minutes

Paste the script into your theme once, then add the form with a Custom Liquid section.

  1. List your shop

    In CETECommand: JobsSettingsBooking page. Under Your websites add your own domain and your your-shop.myshopify.com address. Save.

  2. Open the code editor

    In Shopify admin: Online StoreThemes. Next to your current theme press the ... button, then Edit code.

  3. Add the script

    Open layout/theme.liquid. Find </body> near the bottom and paste this just above it. Save.

    theme.liquid, before </body>
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>

    Leave out data-chat if you do not want the chat button.

  4. Add the form

    Back in Online StoreThemes, press Customize. Open your Contact page, then Add section, Custom Liquid. Paste this and save.

    Custom Liquid section
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  5. Add a client login link

    Optional. Online StoreNavigationMain menu, Add menu item. Name it Client login and use this link:

    Client login link
    https://app.cetechat.com/p/YOUR_BOOKING_KEY

The form shows on your live shop. The theme editor preview runs on a Shopify address, so it may show a blank space there.

Wix

About 10 minutes

Add the form with Embed a Site, and the chat with Custom code.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Under Your websites add your Wix address (for example yourname.wixsite.com) and your own domain if you have one connected. Save. The form and chat only show on the websites you list.

  2. Add the form

    In the Wix Editor: Add Elements (+), Embed Code, Embed a Site. Paste this address and press Apply:

    Website address
    https://app.cetechat.com/e/YOUR_BOOKING_KEY/embed

    Drag the box tall enough for the form, about 900 pixels (more with photos). Wix keeps embeds at a fixed height.

    For maintenance requests only, add ?type=work_order to the end. For quotes only, ?type=quote. Use Embed a Site, not Embed HTML: Wix runs pasted HTML from a different address, so the form would not show.

  3. Chat on every page

    In your Wix dashboard: SettingsCustom code (under Advanced). Press + Add Custom Code and paste this. Choose All pages, Load code once, place it in Body - end, then Apply.

    Custom code, Body - end
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>

    Custom code needs a Wix Premium plan with your own domain connected. Make sure that domain is under Your websites.

  4. Add a client login button

    Optional. Add a Button, then Link, Web address, and paste:

    Client login link
    https://app.cetechat.com/p/YOUR_BOOKING_KEY
  5. Publish

    Press Publish. The form and chat show on your published site, not in the Editor or its preview (those run on a Wix address you have not listed).

Squarespace

About 5 minutes

Add the script with Code Injection, and the form with a Code block.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Add the script

    In Squarespace: WebsitePagesCustom CodeCode Injection (on older sites: SettingsAdvancedCode Injection). Paste this into Footer and save.

    Code Injection, Footer
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>

    Code Injection and Code blocks with scripts need a Core plan or higher (Business on older plans).

  3. Add the form

    Edit your Contact page, press Add Block, choose Code and paste this. Turn off Display Source, then save.

    Code block
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  4. Add a client login

    Optional. Another Code block with this, or a button linking to https://app.cetechat.com/p/YOUR_BOOKING_KEY.

    Client login
    <div data-cete="client-login"></div>

Webflow

About 5 minutes

Add the script in your project settings, and the form with an Embed element.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

    Add your webflow.io address too if you want to test on it.

  2. Add the script

    In Webflow: Site settingsCustom code. Paste this into Footer code and save.

    Footer code
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>

    Custom code needs a paid Webflow site plan.

  3. Add the form

    In the Designer, open the Add panel, drag a Code Embed element onto your page, paste this and save.

    Code Embed
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  4. Publish

    Press Publish. Scripts do not run in the Designer, so check the form on your published site.

Plain HTML

About 2 minutes

Two lines: the script once on every page, and a div where the form goes.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Add the script to every page

    Paste it just before </body>, or in <head>. It loads after the page, so it does not slow it down.

    Every page
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>
  3. Put the form where you want it

    Enquiry form
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

    Pick a service, change the heading
    <div data-cete="enquiry-form" data-service="Roofing" data-title="Get a roof quote"></div>
  4. Your own chat button and client login

    Optional. The chat button sits in the corner by itself. To open it from your own button as well:

    Chat and client login
    <button type="button" id="chat-with-us">Chat with us</button>
    <script>
      document.getElementById('chat-with-us').addEventListener('click', function () {
        if (window.CETE) window.CETE.openChat();
      });
    </script>
    
    <!-- A "Client login" link to your client portal -->
    <div data-cete="client-login" data-label="Client login"></div>
  5. Know when a form is sent

    Optional, for analytics. The form element fires a cete:submitted event.

    Form sent event
    <script>
      document.addEventListener('cete:submitted', function (e) {
        console.log('Enquiry sent:', e.detail.kind);
      });
    </script>

React / Next.js

About 5 minutes

Load the script once, then drop the form div into any component. It finds forms added later by React.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

    Add localhost:3000 (or your dev port) while you build.

  2. Next.js: load the script in your root layout

    app/layout.tsx
    import Script from 'next/script';
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en-AU">
          <body>
            {children}
            <Script
              src="https://app.cetechat.com/w/v1.js"
              data-key="YOUR_BOOKING_KEY"
              data-chat=""
              strategy="afterInteractive"
            />
          </body>
        </html>
      );
    }

    Vite or Create React App: paste the plain script tag into index.html instead.

  3. Put the form in a component

    Contact.tsx
    export default function Contact() {
      return (
        <section>
          <h1>Get a quote</h1>
          <div data-cete="enquiry-form" data-type="quote" data-service="Roofing" />
          <div data-cete="client-login" />
        </section>
      );
    }
  4. Open the chat from your own button

    ChatButton.tsx
    declare global { interface Window { CETE?: { openChat: () => void } } }
    
    export function ChatButton() {
      return <button type="button" onClick={() => window.CETE?.openChat()}>Chat with us</button>;
    }

    Want a typed component? See <EnquiryForm /> in the Node.js package.

Vue / Nuxt

About 5 minutes

Load the script once in your app head, then use the form div in any template.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

    Add localhost:5173 (or your dev port) while you build.

  2. Nuxt: add the script to the app head

    nuxt.config.ts
    export default defineNuxtConfig({
      app: {
        head: {
          script: [
            { src: 'https://app.cetechat.com/w/v1.js', 'data-key': 'YOUR_BOOKING_KEY', 'data-chat': '', async: true },
          ],
        },
      },
    });

    Plain Vue with Vite: paste the script tag into index.html.

  3. Put the form in a template

    pages/contact.vue
    <template>
      <section>
        <h1>Get a quote</h1>
        <div data-cete="enquiry-form" data-type="quote"></div>
        <div data-cete="client-login"></div>
      </section>
    </template>

    The script watches the page, so forms on pages opened later by the router show too.

PHP / Laravel

About 5 minutes

Add the script to your layout. For your own forms, send enquiries from the server with the API.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Laravel: add the script to your layout

    resources/views/layouts/app.blade.php
        {{-- before </body> --}}
        <script src="https://app.cetechat.com/w/v1.js" data-key="{{ config('services.cete.booking_key') }}" data-chat async></script>
    </body>
    .env
    CETE_BOOKING_KEY=YOUR_BOOKING_KEY
    config/services.php
    'cete' => [
        'booking_key' => env('CETE_BOOKING_KEY'),
    ],

    Plain PHP: paste the script tag into your shared footer file, such as footer.php. With our PHP package (below) in a Laravel app, @cetecommandScript and @cetecommandEnquiryForm do the same from CETECOMMAND_BOOKING_KEY.

  3. Put the form in a view

    resources/views/contact.blade.php
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  4. Keep your own form?

    Send its entries to CETECommand from your controller. See PHP and Laravel below.

Node.js

About 5 minutes

Express, Fastify or any Node site: the script goes in your page template. Your server can talk to the API too.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Add the script to your layout template

    In EJS, Handlebars, Pug or plain HTML, before </body>:

    views/layout.ejs
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>
  3. Put the form on a page

    views/contact.ejs
    <div data-cete="enquiry-form"></div>

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  4. Send enquiries from your own form

    Use the API from your server. See Node.js below.

Python

About 5 minutes

Django, Flask or FastAPI: add the script to your base template.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Add the script to your base template

    templates/base.html
        {# before </body> #}
        <script src="https://app.cetechat.com/w/v1.js" data-key="{{ cete_booking_key }}" data-chat async></script>
    </body>

    Pass cete_booking_key from your settings, or paste the key in. It is public, so that is fine.

  3. Put the form on a page

    templates/contact.html
    {% extends "base.html" %}
    {% block content %}
      <div data-cete="enquiry-form"></div>
    {% endblock %}

    Only quotes: add data-type="quote". Only maintenance requests: data-type="work_order". Leave it out and customers choose (when maintenance requests are on).

  4. Send enquiries from your own form

    A few lines with requests. See Python below.

Any other website

About 5 minutes

Joomla, Drupal, GoDaddy, Weebly, Framer, Carrd, Duda and more. If you can paste code, it works.

  1. List your site

    In CETECommand: JobsSettingsBooking page. Add your website address under Your websites and save. The form and chat only show on the websites you list.

  2. Find the code spot

    Most website builders call it Custom code, Code injection, Header and footer scripts or Tracking code. Paste the script there so it is on every page.

    Every page
    <script src="https://app.cetechat.com/w/v1.js" data-key="YOUR_BOOKING_KEY" data-chat async></script>

    Leave out data-chat if you do not want the chat button.

  3. Add the form

    Use an HTML, Embed or Code block where you want the form, and paste one of these:

    Quote or maintenance (they choose)
    <div data-cete="enquiry-form"></div>
    Quotes only
    <div data-cete="enquiry-form" data-type="quote"></div>
    Maintenance requests only
    <div data-cete="enquiry-form" data-type="work_order"></div>
  4. Add a client login

    Client login
    <div data-cete="client-login"></div>

    Or link a menu item or button straight to https://app.cetechat.com/p/YOUR_BOOKING_KEY.

  5. Builder runs embeds in a frame?

    Some builders run pasted code from an address of their own, so the form does not show. If yours does, or it only takes a web address, embed the form page itself with its "embed a website" element. Your website address must still be under Your websites.

    Form address
    https://app.cetechat.com/e/YOUR_BOOKING_KEY/embed

No website at all? Share your booking page: https://app.cetechat.com/e/YOUR_BOOKING_KEY. Put it on Google, Facebook and your email signature.

For developers

Your own forms, your own code.

Send enquiries from your own forms, read jobs, customers, quotes and invoices, and hear about changes with signed webhooks.

Node.js

Our package wraps the REST API, checks webhook signatures and gives you a React form component. Use it from your server only: API keys are secret.

Install Coming soon
npm install cetecommand
Send an enquiry Coming soon
import { CeteCommand } from 'cetecommand';

const cete = new CeteCommand({ apiKey: process.env.CETE_API_KEY });

const enquiry = await cete.enquiries.create({
  name: 'Sam Taylor',
  email: '[email protected]',
  phone: '0400 000 000',
  address: '12 Smith St, Parramatta NSW 2150',
  service: 'Bathroom renovation',
  message: 'Shower is leaking, needs regrouting.',
});
console.log('Enquiry', enquiry.id);
List jobs Coming soon
const { data: jobs, hasMore, nextBefore } = await cete.jobs.list({ limit: 25 });

for (const job of jobs) {
  console.log(job.number, job.title, job.status);
}
// Next page: cete.jobs.list({ before: nextBefore })
// Or every open job, page by page:
for await (const job of cete.jobs.iterate({ status: 'open' })) {
  console.log(job.number, job.title);
}
Verify a webhook (Express) Coming soon
import express from 'express';
import { verifyWebhook } from 'cetecommand';

const app = express();

// Before app.use(express.json()): the signature is checked on the raw body.
app.post('/cete/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = verifyWebhook({
      payload: req.body,
      headers: req.headers,
      secret: process.env.CETE_WEBHOOK_SECRET,
    });
  } catch (err) {
    return res.status(400).json({ error: err.message });
  }

  if (event.type === 'enquiry.created') {
    console.log('New enquiry from', event.data.name);
  }
  res.sendStatus(200);
});

app.listen(3000);
React form component Coming soon
import { EnquiryForm } from 'cetecommand/react';

export default function Contact() {
  return (
    <EnquiryForm
      bookingKey="YOUR_BOOKING_KEY"
      type="quote"
      service="Roofing"
      onSubmitted={(detail) => console.log('Sent', detail.kind)}
    />
  );
}
Your own form, booking key Coming soon
import { sendEnquiry } from 'cetecommand';

// No API key: the booking key is public. Throws CeteCommandError on a problem.
const { message } = await sendEnquiry('YOUR_BOOKING_KEY', {
  name: 'Sam Taylor',
  email: '[email protected]',
  service: 'Bathroom renovation',
  message: 'Shower is leaking, needs regrouting.',
});
console.log(message); // your thank you message

The React component and sendEnquiry use your public booking key, not an API key, so they are safe in the browser (import sendEnquiry from cetecommand/enquiry there). The booking key takes up to 8 enquiries an hour from one address. From your server every enquiry comes from one address, so for a busy form use cete.enquiries.create() with an API key instead.

PHP and Laravel

A small client for the REST API and webhook signatures. Works on any site with PHP 7.4 or later, with or without Composer, and has Laravel support built in (Laravel 8 or later).

Install Coming soon
composer require cetecommand/cetecommand
Send an enquiry Coming soon
<?php
require 'vendor/autoload.php';

$cete = new \CeteCommand\Client(getenv('CETE_API_KEY'));

$enquiry = $cete->enquiries()->create([
    'name' => 'Sam Taylor',
    'email' => '[email protected]',
    'phone' => '0400 000 000',
    'service' => 'Bathroom renovation',
    'message' => 'Shower is leaking, needs regrouting.',
]);
echo $enquiry['id'];
List jobs and verify a webhook Coming soon
$jobs = $cete->jobs()->list(['limit' => 25]);
foreach ($jobs['data'] as $job) {
    echo $job['number'] . ' ' . $job['title'] . PHP_EOL;
}
// Next page: $cete->jobs()->list(['before' => $jobs['nextBefore']])

// webhook.php
try {
    $event = \CeteCommand\Webhook::verify(
        file_get_contents('php://input'),
        $_SERVER['HTTP_X_CETE_SIGNATURE'] ?? '',
        getenv('CETE_WEBHOOK_SECRET')
    );
} catch (\CeteCommand\Exception\SignatureException $e) {
    http_response_code(400);
    exit;
}
// $event['type'], $event['data']
Your own form, booking key Coming soon
use CeteCommand\Enquiry;

// No API key: the booking key is public. Up to 8 an hour from your server.
$answer = Enquiry::send('YOUR_BOOKING_KEY', [
    'name' => 'Sam Taylor',
    'email' => '[email protected]',
    'message' => 'Shower is leaking, needs regrouting.',
]);
echo $answer['message']; // your thank you message
Laravel: .env
CETECOMMAND_API_KEY=YOUR_API_KEY
CETECOMMAND_BOOKING_KEY=YOUR_BOOKING_KEY
CETECOMMAND_WEBHOOK_SECRET=YOUR_WEBHOOK_SECRET
CETECOMMAND_CHAT=true
Laravel: your contact form into CETECommand Coming soon
// app/Http/Controllers/ContactController.php
use CeteCommand\Client;
use Illuminate\Http\Request;

public function store(Request $request, Client $cete)
{
    $data = $request->validate([
        'name' => 'required|max:120',
        'email' => 'required|email',
        'phone' => 'nullable|max:40',
        'message' => 'required|max:5000',
    ]);

    // The client comes set up from CETECOMMAND_API_KEY
    $cete->enquiries()->create($data + [
        'site' => $request->getHost(),
        'ip' => $request->ip(),
    ]);

    return back()->with('status', 'Thanks, we will be in touch soon.');
}
Laravel: webhook route Coming soon
// routes/api.php (no CSRF check here)
use Illuminate\Http\Request;

Route::post('/cete/webhook', function (Request $request) {
    // Checked against CETECOMMAND_WEBHOOK_SECRET by the middleware (400 if not)
    $event = $request->attributes->get('cetecommand_event');
    // $event['type'] is enquiry.created, job.created, invoice.paid ...
    return response()->noContent();
})->middleware('cetecommand.webhook');
Laravel: Blade Coming soon
{{-- In your layout, once (the chat button too, with CETECOMMAND_CHAT=true) --}}
@cetecommandScript

{{-- Where the form goes --}}
@cetecommandEnquiryForm(['type' => 'quote', 'service' => 'Roofing'])

{{-- A client portal link --}}
@cetecommandClientLogin(['label' => 'Client login'])

Shared hosting without Composer? Upload cetecommand.php and the src folder side by side and require cetecommand.php instead of vendor/autoload.php.

Python

No package yet: requests and the standard library do the job.

Send an enquiry (booking key)
import requests

r = requests.post(
    "https://app.cetechat.com/e/YOUR_BOOKING_KEY/api",
    json={
        "name": "Sam Taylor",
        "email": "[email protected]",
        "phone": "0400 000 000",
        "address": "12 Smith St, Parramatta NSW 2150",
        "service": "Bathroom renovation",
        "message": "Shower is leaking, needs regrouting.",
        "preferred": "Weekday mornings",
    },
    timeout=10,
)
r.raise_for_status()
print(r.json()["message"])  # your thank you message
A maintenance request
requests.post(
    "https://app.cetechat.com/e/YOUR_BOOKING_KEY/api",
    json={
        "kind": "work_order",
        "name": "Priya Shah",
        "email": "[email protected]",
        "company": "Harbour Strata",
        "orderNumber": "PO-4471",
        "address": "8/22 Bay Rd, Wollongong NSW 2500",
        "lot": "8",
        "strataPlan": "SP12345",
        "tenantName": "Tom",
        "tenantPhone": "0400 111 222",
        "access": "Key in the lockbox, code 1234",
        "urgency": "urgent",  # routine, soon, urgent or emergency
        "message": "Hot water system leaking.",
    },
    timeout=10,
).raise_for_status()
Verify a webhook (Flask)
import hashlib, hmac, os, time
from flask import Flask, abort, request

app = Flask(__name__)
SECRET = os.environ["CETE_WEBHOOK_SECRET"]  # YOUR_WEBHOOK_SECRET

def verify(raw: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    t, sig = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or abs(time.time() - int(t)) > 300:
        return False
    want = hmac.new(secret.encode(), t.encode() + b"." + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, sig)

@app.post("/cete/webhook")
def cete_webhook():
    if not verify(request.get_data(), request.headers.get("X-Cete-Signature", ""), SECRET):
        abort(400)
    event = request.get_json()
    if event["type"] == "enquiry.created":
        print("New enquiry from", event["data"]["name"])
    return "", 200
List jobs (API key)
import os, requests

r = requests.get(
    "https://app.cetechat.com/v1/jobs",
    headers={"Authorization": f"Bearer {os.environ['CETE_API_KEY']}"},
    params={"limit": 25},
    timeout=10,
)
r.raise_for_status()
for job in r.json()["data"]:
    print(job["number"], job["title"], job["status"])

The booking key address needs no secret, and from a browser it only takes enquiries from your listed websites. It takes up to 8 enquiries an hour from one address, though, and from your server every enquiry comes from one address. For a busy form, send them with an API key instead: POST https://app.cetechat.com/v1/enquiries with the same fields.

REST API and webhooks

Make a key in CETECommand: JobsSettingsAPI and webhooks. Send it as a Bearer token from your server. 120 requests a minute per key. Lists take ?limit= (up to 100) and ?before= to page back.

Check your key
curl https://app.cetechat.com/v1/ping \
  -H "Authorization: Bearer YOUR_API_KEY"
API endpoints
MethodPathWhat it does
GET/v1/pingCheck your key
GET POST/v1/enquiriesList or add enquiries
GET/v1/enquiries/:idOne enquiry
GET POST/v1/customersList (by email or search) or add customers
GET/v1/customers/:idOne customer
GET/v1/jobsList jobs
GET/v1/jobs/:idOne job
GET/v1/documentsList quotes and invoices
GET/v1/documents/:idOne quote or invoice, with its link

Add an HTTPS address in the same place and pick events. Each one is a signed JSON POST: X-Cete-Event, X-Cete-Delivery and X-Cete-Signature: t=<time>,v1=<HMAC-SHA256 of "t.body">. Answer with a 2xx within 8 seconds.

Webhook events
EventWhen
enquiry.createdA new enquiry (website form, booking page, email, chat or API)
webchat.createdA visitor started a website chat
job.createdA job was made
job.status_changedA job moved to another status
quote.acceptedA customer accepted a quote or work order
quote.declinedA customer declined a quote or work order
invoice.paidAn invoice was paid in full

Full developer docs

What lands where

Nothing gets lost.

Each one comes with a note in your team chat, so the right person sees it straight away.

Enquiry formJobs, Enquiries

One tap turns it into a customer and a job, with the photos and access notes already on it.

Website chatJobs, Website chat

Answer from your phone or the office. Turn a chat into an enquiry when they want a quote.

Shop ordersInvoicing

Each order becomes an invoice. Paid orders are marked paid, and customers are matched by email.

Questions

Straight answers.

Is it free?

Yes. The enquiry form, website chat and client portal work on every plan, including Free. Shop orders into Invoicing are on paid plans and in your free months.

Does it slow my site down?

No. It is one small script that loads after your page. The form only loads where you put it, and the chat button only when you turn chat on.

Is it secure?

Yes. Your booking key is public on purpose: it is already in your booking page address, and it only lets your listed websites show your form and send you enquiries. API keys are different. They are secret, like a password. Only use them on a server, and never put them in browser code.

Do I need a developer?

Not for most websites. WordPress has a plugin, and Shopify, Wix, Squarespace and Webflow are copy and paste. A developer only helps if you want your own form, a custom design, or to connect other software with the API.

The form does not show. What now?

Check your website address is listed under Your websites in Jobs, Settings, Booking page (no need for https or www), and that you saved. Website builders often skip scripts in their editor preview, so check your published site.

Can my web person do it for me?

Yes. Send them a link to this page and the code from Website code. They do not need a login to CETECommand.

Your website, working for you.

Launch offer: the first 100 businesses get 3 months of everything free, no card. Set up your website form today.

Questions? Talk to us.