Developer reference
Gas Checker REST API
Versioned JSON API for the Flutter app. Base URL: https://your-domain.com/api/v1. All requests and responses use JSON except file uploads.
Envelope
Every response is { data, error, meta }.
Bearer auth
Authorization: Bearer <access_token>
Trial aware
402 responses include subscription state.
Response format
// Success
{ "data": { "id": "..." }, "error": null, "meta": null }
// List with pagination
{ "data": [ ... ], "error": null, "meta": { "count": 42, "limit": 50, "offset": 0 } }
// Failure
{ "data": null, "error": { "code": "SUBSCRIPTION_REQUIRED", "message": "...", "details": null } }/subscription/checkout; do not treat it as a network failure.Authentication
Signup and login return a Supabase access token and refresh token. Send the access token as a Bearer header on every protected request. Refresh when a request returns 401.
POST/api/v1/auth/signupCreate an account and start the 30-day Professional trial.
Create an account and start the 30-day Professional trial.
{
"email": "engineer@example.com",
"password": "min8chars",
"full_name": "Kabbo Hasan",
"company_name": "Hasan Heating",
"phone": "07700 900000"
}POST/api/v1/auth/loginSign in and receive tokens plus the account, plan and trial state.
Sign in and receive tokens plus the account, plan and trial state.
{ "email": "engineer@example.com", "password": "min8chars" }POST/api/v1/auth/refreshExchange a refresh token for a new session.
Exchange a refresh token for a new session.
{ "refresh_token": "<refresh_token>" }GET/api/v1/auth/meCurrent user, settings, subscription and trial days.
Current user, settings, subscription and trial days.
Requires Bearer token
POST/api/v1/auth/logoutRevoke the current token globally.
Revoke the current token globally.
Requires Bearer token
Plans & subscription
Read subscription state anywhere in the app. Creating business data requires an active plan; after the trial ends protected writes return 402 SUBSCRIPTION_REQUIRED.
GET/api/v1/plansPublic plan catalogue with monthly and yearly prices.
Public plan catalogue with monthly and yearly prices.
GET/api/v1/subscriptionCurrent subscription, plan and trial days left.
Current subscription, plan and trial days left.
Requires Bearer token
POST/api/v1/subscription/checkoutStart Stripe Checkout, or activate directly when Stripe is not configured.
Start Stripe Checkout, or activate directly when Stripe is not configured.
{ "plan_id": "pro", "billing_interval": "monthly" }Requires Bearer token
POST/api/v1/subscription/cancelCancel at the end of the current period.
Cancel at the end of the current period.
Requires Bearer token
POST/api/v1/subscription/resumeUndo a pending cancellation.
Undo a pending cancellation.
Requires Bearer token
Contacts
Clients, installations and suppliers. Search across name, email, phone and postcode.
GET/api/v1/contactsList contacts.
List contacts.
Query: ?search=patel&type=client&limit=50&offset=0
Requires Bearer token
POST/api/v1/contactsCreate a contact.
Create a contact.
{ "name": "Mrs Patel", "phone": "07700 900000", "postcode": "M1 1AE", "type": "client" }Requires Bearer token
GET/api/v1/contacts/:idFetch one contact.
Fetch one contact.
Requires Bearer token
PATCH/api/v1/contacts/:idUpdate supplied fields.
Update supplied fields.
Requires Bearer token
DELETE/api/v1/contacts/:idDelete a contact.
Delete a contact.
Requires Bearer token
Invoices & quotes
Invoices and quotes share the same document model and line items. Totals are calculated server-side from quantity × unit price plus tax.
GET/api/v1/invoicesList invoices with items and payments.
List invoices with items and payments.
Query: ?status=overdue&search=INV&limit=50&offset=0
Requires Bearer token
POST/api/v1/invoicesCreate an invoice. Set type to quote on /quotes.
Create an invoice. Set type to quote on /quotes.
{
"client_name": "Mrs Patel",
"issue_date": "2026-10-25",
"due_date": "2026-11-08",
"tax_rate": 20,
"items": [
{ "description": "Boiler service", "quantity": 1, "unit_price": 85 }
]
}Requires Bearer token
GET/api/v1/invoices/:idFetch an invoice with items and payments.
Fetch an invoice with items and payments.
Requires Bearer token
PATCH/api/v1/invoices/:idUpdate header fields or status.
Update header fields or status.
Requires Bearer token
DELETE/api/v1/invoices/:idDelete an invoice and its items.
Delete an invoice and its items.
Requires Bearer token
GET/api/v1/invoices/:id/paymentsList payments for an invoice.
List payments for an invoice.
Requires Bearer token
POST/api/v1/invoices/:id/paymentsRecord a payment. Invoice status and balance are recalculated automatically.
Record a payment. Invoice status and balance are recalculated automatically.
{ "amount": 85, "method": "bank", "reference": "BACS-123" }Requires Bearer token
GET/api/v1/quotesList quotes.
List quotes.
Query: ?status=accepted&search=QT
Requires Bearer token
POST/api/v1/quotesCreate a quote using the same body as an invoice.
Create a quote using the same body as an invoice.
Requires Bearer token
GET/api/v1/quotes/:idFetch a quote with items.
Fetch a quote with items.
Requires Bearer token
PATCH/api/v1/quotes/:idUpdate a quote.
Update a quote.
Requires Bearer token
DELETE/api/v1/quotes/:idDelete a quote.
Delete a quote.
Requires Bearer token
POST/api/v1/quotes/:id/convertConvert a quote into a new draft invoice and mark the quote accepted.
Convert a quote into a new draft invoice and mark the quote accepted.
Requires Bearer token
Schedule & reminders
Calendar appointments and the follow-up reminders shown on the home screen.
GET/api/v1/appointmentsList appointments, optionally by date range and status.
List appointments, optionally by date range and status.
Query: ?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&status=scheduled
Requires Bearer token
POST/api/v1/appointmentsCreate an appointment.
Create an appointment.
{
"title": "Landlord CP12",
"location": "42 Elm Road",
"starts_at": "2026-10-25T14:00:00Z",
"alert_minutes": 60,
"contact_id": null
}Requires Bearer token
PATCH/api/v1/appointments/:idUpdate or mark an appointment complete.
Update or mark an appointment complete.
Requires Bearer token
DELETE/api/v1/appointments/:idDelete an appointment.
Delete an appointment.
Requires Bearer token
GET/api/v1/remindersList reminders.
List reminders.
Query: ?status=pending&kind=invoice
Requires Bearer token
POST/api/v1/remindersCreate a reminder for a gas report, invoice or custom task.
Create a reminder for a gas report, invoice or custom task.
{ "title": "Chase invoice", "kind": "invoice", "due_date": "2026-11-01" }Requires Bearer token
PATCH/api/v1/reminders/:idMark done, dismissed or edit.
Mark done, dismissed or edit.
Requires Bearer token
DELETE/api/v1/reminders/:idDelete a reminder.
Delete a reminder.
Requires Bearer token
Gas reports
Gas safety certificates, warning notices and service records. Appliances, defects and readings are stored as JSON so the Flutter form can evolve without schema changes.
GET/api/v1/report-typesStatic list of supported certificate types and icons.
Static list of supported certificate types and icons.
Requires Bearer token
GET/api/v1/reportsList reports.
List reports.
Query: ?status=completed&type=domestic-landlord-gas-safety&search=CP12
Requires Bearer token
POST/api/v1/reportsCreate a report.
Create a report.
{
"type": "domestic-landlord-gas-safety",
"reference": "CP12-0042",
"status": "draft",
"property_postcode": "M1 1AE",
"appliances": [{ "name": "Worcester 8000", "kw": 24 }],
"readings": {}
}Requires Bearer token
GET/api/v1/reports/:idFetch one report.
Fetch one report.
Requires Bearer token
PATCH/api/v1/reports/:idUpdate a report. Completed reports stamp completed_at.
Update a report. Completed reports stamp completed_at.
Requires Bearer token
DELETE/api/v1/reports/:idDelete a report.
Delete a report.
Requires Bearer token
Gas rate & dashboard
The calculator can return an unsaved preview or save the calculation to history.
POST/api/v1/gas-rateCalculate gross and net kW. Send save: false for a preview.
Calculate gross and net kW. Send save: false for a preview.
{ "meter_type": "metric", "reading1": 0, "reading2": 0.02, "seconds": 42, "save": true }Requires Bearer token
GET/api/v1/gas-rateCalculation history, newest first.
Calculation history, newest first.
Query: ?limit=20&offset=0
Requires Bearer token
GET/api/v1/dashboard/summaryCounts, outstanding balance, upcoming jobs and reminders.
Counts, outstanding balance, upcoming jobs and reminders.
Requires Bearer token
Account, uploads & backup
Settings drive every generated document. Files are stored in the private Supabase bucket and returned as signed URLs.
GET/api/v1/settingsBusiness settings and document prefixes.
Business settings and document prefixes.
Requires Bearer token
PATCH/api/v1/settingsUpdate any supplied settings field.
Update any supplied settings field.
Requires Bearer token
PATCH/api/v1/profileUpdate name, company, phone, Gas Safe ID or avatar.
Update name, company, phone, Gas Safe ID or avatar.
Requires Bearer token
POST/api/v1/uploadsMultipart upload with fields file and folder (max 15 MB; JPEG, PNG, WebP, HEIC, PDF).
Multipart upload with fields file and folder (max 15 MB; JPEG, PNG, WebP, HEIC, PDF).
Requires Bearer token
GET/api/v1/uploads/signCreate a temporary signed URL.
Create a temporary signed URL.
Query: ?path=<storage_path>&expires=3600
Requires Bearer token
POST/api/v1/device-tokensRegister an FCM token for push notifications.
Register an FCM token for push notifications.
{ "token": "<fcm_token>", "platform": "android" }Requires Bearer token
GET/api/v1/backupExport all data. Add raw=true to download a JSON file.
Export all data. Add raw=true to download a JSON file.
Requires Bearer token
POST/api/v1/backupStore a copy in Supabase Storage and record it in history.
Store a copy in Supabase Storage and record it in history.
Requires Bearer token
POST/api/v1/backup/restoreRestore a previous export. Merge keeps current data; replace deletes it first.
Restore a previous export. Merge keeps current data; replace deletes it first.
{ "backup": { "version": 1, "data": { "...": "..." } }, "mode": "merge" }Requires Bearer token
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_JSON | The request body is not valid JSON. |
| 401 | UNAUTHORIZED / INVALID_TOKEN | Missing, expired or invalid access token. |
| 402 | SUBSCRIPTION_REQUIRED | Trial ended and no active plan. Send the user to billing. |
| 403 | FORBIDDEN | The record belongs to another user. |
| 404 | NOT_FOUND | Record does not exist for this user. |
| 413 | FILE_TOO_LARGE | Upload exceeds 15 MB. |
| 415 | UNSUPPORTED_FILE | Only images or PDF files are allowed. |
| 422 | VALIDATION_ERROR | Field validation failed; details contains the field errors. |
| 503 | DATABASE_NOT_READY | Supabase migration or exposed schema has not been applied. |
Flutter client
Store both tokens securely, attach the access token to each call, and refresh once on a 401 before signing the user out.
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiClient {
ApiClient({required this.baseUrl, required this.getToken, required this.onRefresh});
final String baseUrl; // https://your-domain.com/api/v1
final Future<String?> Function() getToken;
final Future<bool> Function() onRefresh;
Future<Map<String, dynamic>> request(
String method,
String path, {
Map<String, dynamic>? body,
bool retried = false,
}) async {
final token = await getToken();
final response = await http.Request(method, Uri.parse('$baseUrl$path'))
..headers.addAll({
'content-type': 'application/json',
if (token != null) 'authorization': 'Bearer $token',
})
..body = body == null ? '' : jsonEncode(body);
final streamed = await response.send();
final decoded = jsonDecode(await streamed.stream.bytesToString());
if (streamed.statusCode == 401 && !retried && await onRefresh()) {
return request(method, path, body: body, retried: true);
}
if (streamed.statusCode >= 400) {
throw ApiException(
code: decoded['error']?['code'] ?? 'UNKNOWN',
message: decoded['error']?['message'] ?? 'Request failed',
status: streamed.statusCode,
);
}
return decoded['data'] as Map<String, dynamic>;
}
}For multipart uploads use MultipartRequest to /api/v1/uploads with fields file and folder.
Ready to power your Flutter app?
Create an account to receive a trial token pair.