Fønix FX API

A REST API over the Fønix inspection database. Read and write customers, structures, objects, certificates, inspections and sales from any application.

Open the interactive API explorer Download the OpenAPI document

1. Getting an API key

Every request must carry an API key in the X-Api-Key header. Keys are issued by the API administrator and are bound to a set of scopes:

ScopeWhat it allows
readGET on every resource and view.
writeEverything read allows, plus POST, PUT and DELETE.
Treat the key like a password. Keep it on your server or in the OS keychain — never ship it inside a browser bundle or a mobile app binary, and never commit it to source control.

2. Your first request

curl -H "X-Api-Key: YOUR_KEY" \
     "https://localhost:7262/api/v1/customers?page=1&pageSize=25"

Every collection response has the same envelope:

{
  "items": [ { "id": 1, "customerNo": "1001", "customerName": "Acme Ltd" } ],
  "page": 1,
  "pageSize": 25,
  "totalItems": 842,
  "totalPages": 34,
  "hasPrevious": false,
  "hasNext": true
}

3. The five operations

Every table resource supports the same five calls. {resource} is one of the paths listed in section 5.

CallScopeDescription
GET /api/v1/{resource}readList records, with paging, filtering, search and sorting.
GET /api/v1/{resource}/{id}readFetch one record by its id.
POST /api/v1/{resource}writeCreate a record. Returns 201 and the created row (with its new id).
PUT /api/v1/{resource}/{id}writeUpdate a record. The id comes from the URL; an id in the body is ignored.
DELETE /api/v1/{resource}/{id}writeDelete a record. Returns 204.

Resources under /api/v1/views/… are database views and are read-only (list only).

4. Querying a collection

ParameterMeaning
page1-based page number. Default 1.
pageSizeRows per page, 1–200. Default 50.
sortColumn name to sort by, e.g. sort=CustomerName.
orderasc (default) or desc.
qFree-text search across all text columns of the resource.
any column nameExact-match filter, e.g. ?CustomerNo=1001&Active=true.
GET /api/v1/objects?StructureID=42&sort=SerialNo&order=desc&pageSize=100
GET /api/v1/customers?q=acme
GET /api/v1/views/structure-certificates?CustomerNo=1001
Unknown column names are rejected with 400 Bad Request. Values are always sent to SQL Server as bound parameters, so filters can never be used for SQL injection.

5. Available resources

Customers

  • customers
  • customer-contacts
  • customer-structures
  • customer-structure-contacts

Structures

  • structures
  • structure-types

Objects

  • objects
  • object-brands
  • object-photos
  • object-types
  • object-type-groups
  • object-size-types
  • object-guids

Certificates

  • certificates
  • certificate-lines
  • certificate-orders
  • certificate-types
  • certificate-service-statuses
  • cp-certificate-lines
  • cp-certificate-line-templates

Inspections

  • inspection-types
  • works
  • reports

Service personnel & sales

  • service-personnel
  • service-personnel-certificates
  • sales

Reference data

  • zips
  • settings

Views (read-only)

  • 36 reporting views under /api/v1/views/…

The API explorer lists every resource with its exact column names and types.

6. Creating and updating

curl -X POST "https://localhost:7262/api/v1/customers" \
     -H "X-Api-Key: YOUR_KEY" \
     -H "Content-Type: application/json" \
     -d '{ "customerNo": "1099", "customerName": "New Customer" }'

Only the resource's own columns are accepted. Related collections are never read from the body, and the primary key is always assigned by the database on create and taken from the URL on update.

7. Errors

Failures use RFC 7807 problem+json.

StatusMeaning
400Unknown filter/sort column, bad value, or invalid paging.
401Missing or invalid X-Api-Key.
403Key is valid but lacks the required scope (usually write).
404No record with that id.
409The change violates a database constraint (duplicate key, missing parent row…).
429Rate limit exceeded — slow down and retry.
500Unexpected server error. Details are logged server-side only.

8. Limits and operational notes

9. Example client (JavaScript)

const BASE = "https://localhost:7262/api/v1";
const KEY  = process.env.FX_API_KEY;   // keep this server-side

async function fx(path, init = {}) {
  const res = await fetch(`${BASE}/${path}`, {
    ...init,
    headers: { "X-Api-Key": KEY, "Content-Type": "application/json", ...init.headers },
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return res.status === 204 ? null : res.json();
}

const page = await fx("customers?pageSize=25&sort=CustomerName");
const made = await fx("customers", {
  method: "POST",
  body: JSON.stringify({ customerNo: "1099", customerName: "New Customer" }),
});
await fx(`customers/${made.id}`, { method: "DELETE" });