Developer access
Connect to your TES reports
List archived reports, submit scientific results with an explicitly authorized credential, and retrieve the available exports.
01 / Generate token
Start with a one-hour Bearer token
Send your application ID as client-id and the API token as api-key. The returned token is valid for 60 minutes.
curl -X POST "https://technicalengineeringsolutions.com/api/archive/share/generate-token" \
-H "client-id: YOUR_CLIENT_ID" \
-H "api-key: TES_API_KEY"400 client ID is missing.
401 API token is invalid or revoked.
403 client ID does not match the API token.
401 Bearer token expired. Generate another token.
API guide test token
Read-only, five-minute portal test token. It cannot submit scientific results and is separate from the one-hour integration token.
Create, fill, and submit a scientific report
TES grants reports:read, reports:write, and samples:write to the client credential. Create a draft, save its returned report.id, then post samples to that draft. Include client_request_id to connect an enrolled client's PLM request and task; supply task_id if that request has multiple tasks. Update the narrative with PATCH /reports/REPORT_ID if needed.
Every sample needs a unique sample_number and non-empty results. Submit when methodology, findings, recommendations, and at least one result are ready. TES reviews the locked submission and either returns it to draft or publishes the final PDF. Published reports appear in the normal archived list.
400 missing required content. 403 missing write scope. 404 report unavailable to this client. 409 report no longer editable or sample belongs to another report.
curl -sS -X POST \
-H "Authorization: $TES_BEARER_TOKEN" \
-H "Content-Type: application/json" \
"https://technicalengineeringsolutions.com/api/archive/share/reports" \
-d '{"title":"Oil condition monitoring","science_methodology":"Wear metals and viscosity analysis","science_findings":"Iron increased in gearbox sample","science_recommendations":"Inspect gearbox and resample","science_prepared_by":"TES Laboratory"}'curl -sS -X POST \
-H "Authorization: $TES_BEARER_TOKEN" \
-H "Content-Type: application/json" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/samples" \
-d '{
"samples": [{
"sample_number": "S-001",
"sampled_on": "2026-09-20",
"severity": "Normal",
"results": {
"iron_ppm": 18.4,
"copper_ppm": 3.1,
"viscosity_40c_cst": 67.8,
"laboratory_comment": "Within expected operating range"
}
}]
}'curl -sS -X POST \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/submit"Webhooks
Receive report and sample events
Register a public HTTPS endpoint using a one-hour Bearer token from a client credential with reports:read and webhooks:write. The read-only API console token cannot register webhooks. TES returns the signing secret when you create the endpoint; store it on your server. GET /api/client/webhooks lists your endpoints without revealing the secret.
Register an endpoint
curl -sS -X POST \
-H "Authorization: $TES_BEARER_TOKEN" \
-H "Content-Type: application/json" \
"https://technicalengineeringsolutions.com/api/client/webhooks" \
-d '{"endpoint_url":"https://example.com/tes/webhooks","events":["sample_results.available","sample_batch.assigned","sample_trip.status_changed"]}'TES sends JSON POST requests. sample_results.available fires after a scientific report is published or when TES imports results into an existing archive; saving a science draft does not trigger it. sample_batch.assigned fires on new or reassigned batches. sample_trip.status_changed fires when an existing batch changes trip status.
Verify every delivery
import { createHmac, timingSafeEqual } from "node:crypto";
const rawBody = await request.text();
const timestamp = request.headers.get("x-tes-timestamp");
const signature = request.headers.get("x-tes-signature");
if (!timestamp || !Number.isFinite(Number(timestamp)) ||
Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return new Response("Stale delivery", { status: 401 });
}
const expected = "sha256=" + createHmac("sha256", process.env.TES_WEBHOOK_SECRET)
.update(`${timestamp}.${rawBody}`).digest("hex");
const actualBytes = Buffer.from(signature || "");
const expectedBytes = Buffer.from(expected);
if (!timestamp || !signature || actualBytes.length !== expectedBytes.length ||
!timingSafeEqual(actualBytes, expectedBytes)) {
return new Response("Invalid signature", { status: 401 });
}
const event = JSON.parse(rawBody);
// Deduplicate event.event_id (or x-tes-delivery-id) before processing.Compute HMAC-SHA256 over the exact timestamp.rawBody bytes with your signing secret. Compare with x-tes-signature in constant time. Also check that x-tes-timestamp is recent and deduplicate event_id. Reply with a 2xx status to mark success. Failed deliveries are logged, but automatic retries are not currently provided.
02 / List reports
Start with the report list
This returns archived reports available to the authenticated client, including report IDs, sample counts, export links, and the sample-results endpoint.
Use this list for published PDFs. Drafts are listed with ?status=draft, and submissions with ?status=in_review for credentials with reports:write. Sample batches track transport status; they do not create reports.
curl -sS \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports"/api/archive/share/reportsStart here. Returns report IDs plus direct sample and download links./api/archive/share/reportsCreate a client-owned scientific report draft; requires reports:write./api/archive/share/reports/{reportId}Read or edit a scientific draft./api/archive/share/reports/{reportId}/samplesRead structured sample results for one report./api/archive/share/reports/{reportId}/samplesAdd results to a scientific draft; requires samples:write./api/archive/share/reports/{reportId}/submitLock the draft and send it to TES review./api/archive/share/sample-batchesTransport/status route for grouped sample numbers./api/archive/share/sample-batchesUpsert a sample batch and update trip status on matching samples./api/archive/share/reports/{reportId}/download?format=pdfArchived report-file route./api/archive/share/reports/{reportId}/download?format=pdf&source=samplesGenerated sample-results PDF preview./api/archive/share/reports/{reportId}/download?format=csvSpreadsheet route: sample rows when available, otherwise report metadata./api/archive/share/reports/{reportId}/download?format=xlsxExcel route: sample rows when available, otherwise report metadata./api/client/webhooksList or register client webhook endpoints; requires webhooks:write.03 / Use JavaScript
Bring reports into your system
Use this from server-side JavaScript with the Bearer token stored in an environment variable. List reports first, then use the returned id for samples and downloads.
const baseUrl = "https://technicalengineeringsolutions.com";
const headers = {
Authorization: process.env.TES_BEARER_TOKEN,
};
const listResponse = await fetch(baseUrl + "/api/archive/share/reports", { headers });
if (!listResponse.ok) throw new Error("Report list failed");
const { reports } = await listResponse.json();
const report = reports[0];
const reportId = report.id; // Use id, not archive_id or legacy_report_no.
const samplesResponse = await fetch(
baseUrl + `/api/archive/share/reports/${encodeURIComponent(reportId)}/samples`,
{ headers: { ...headers, Accept: "application/json" } },
);
const samples = await samplesResponse.json();
const csvResponse = await fetch(
baseUrl + `/api/archive/share/reports/${encodeURIComponent(reportId)}/download?format=csv`,
{ headers: { ...headers, Accept: "text/csv" } },
);
const csv = await csvResponse.text();PHP
Use PHP cURL
Load the key from the server environment, then request and parse the report list using the PHP cURL extension.
<?php
$bearerToken = getenv("TES_BEARER_TOKEN");
$curl = curl_init("https://technicalengineeringsolutions.com/api/archive/share/reports");
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Accept: application/json", "Authorization: " . $bearerToken],
]);
$body = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($status < 200 || $status >= 300) throw new RuntimeException("TES API request failed");
$reports = json_decode($body, true, 512, JSON_THROW_ON_ERROR)["reports"];04 / Token response
Reuse the Bearer token for one hour
Use the returned value exactly as the Authorization header. After 60 minutes, request a new token. API tokens never enter browser code.
Success
200
Token lifetime
60 minutes
Expiry result
401
const tokenResponse = await fetch("https://technicalengineeringsolutions.com/api/archive/share/generate-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
"client-id": process.env.TES_CLIENT_ID,
"api-key": process.env.TES_API_KEY,
},
});
if (!tokenResponse.ok) throw new Error("TES token exchange failed");
const { token, expiry } = await tokenResponse.json();
const reportsResponse = await fetch("https://technicalengineeringsolutions.com/api/archive/share/reports", {
headers: { Authorization: token },
});05 / Sample results
Read samples by report ID
Use the report id returned by the list endpoint. This is the structured route for integrations that need sample numbers, batch IDs, trip status, severity, timing fields, and laboratory result values.
curl -sS \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/samples"06 / Sample batches
Use batches for transport state
List batches when you need to see grouped sample numbers and current trip status. Post to the same endpoint to assign samples to a batch or move a trip through packed, dispatched, in_transit, delivered_to_lab, received, completed, or an exception state.
curl -sS \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/sample-batches?trip_status=in_transit&limit=50"Batch write
Assign samples or update trip status
Batch writes upsert by batch ID for the authenticated client, update matching sample rows, and can trigger client webhooks for assigned batches or trip-status changes.
curl -sS -X POST \
-H "Authorization: $TES_BEARER_TOKEN" \
-H "Content-Type: application/json" \
"https://technicalengineeringsolutions.com/api/archive/share/sample-batches" \
-d '{
"batch_id": "BATCH-2026-001",
"sample_numbers": ["SPL-001", "SPL-002"],
"trip_status": "dispatched",
"origin": "North Pit",
"destination": "Dar es Salaam laboratory",
"transport_means": "registered_vehicle",
"bus_registration": "T123ABC"
}'07 / Track vehicle
Connect to the tracking page
The signed-in tracking page uses the session-protected bus-tracking endpoint. Add road-leg context to show an operator-provided journey, such as Airport to South Africa, alongside live vehicle data.
const query = new URLSearchParams({
registration: "T123ABC",
minutes: "30",
tracking_status: "road",
origin: "Airport",
destination: "South Africa",
});
const response = await fetch("/api/bus-tracking?" + query, {
credentials: "same-origin",
cache: "no-store",
});
const tracking = await response.json();
if (!response.ok) throw new Error(tracking.error || "Tracking request failed");
console.log(tracking.transport_leg);
console.log(tracking.data);cURL
Use a signed-in session
Use this only from a trusted environment with a current client portal session. Session cookies must never be committed or shared.
curl -sS \
--cookie "tes_client_session=$TES_CLIENT_SESSION" \
"https://technicalengineeringsolutions.com/api/bus-tracking?registration=T123ABC&minutes=30&tracking_status=road&origin=Airport&destination=South%20Africa"08 / Tracking responses
Handle each tracking outcome
Use transport_leg for the operator-provided journey status. The data field contains the unmodified LATRA response and can vary by provider response.
200: Road journey
{
"ok": true,
"registration": "T123ABC",
"start_date_time": "2026-09-13T08:00:00.000Z",
"end_date_time": "2026-09-13T08:30:00.000Z",
"transport_leg": {
"status": "road",
"status_label": "On the road",
"origin": "Airport",
"destination": "South Africa",
"message": "On the road from Airport to South Africa.",
"source": "operator_provided"
},
"data": [
{
"location": "Provider-defined location data"
}
],
"logged": {
"id": "tracking-event-id",
"created_at": "2026-09-13T08:30:01.000Z"
}
}200: Vehicle only
{
"ok": true,
"registration": "T123ABC",
"transport_leg": null,
"data": [
{
"location": "Provider-defined location data"
}
]
}400: Invalid road leg
{
"error": "origin and destination are required for road transport tracking."
}401: No session
{
"error": "Unauthorized"
}503: Tracking unavailable
{
"error": "Bus tracking is not configured."
}502: LATRA unavailable
{
"ok": false,
"registration": "T123ABC",
"error": "LATRA returned 503",
"data": null,
"transport_leg": {
"status": "road",
"status_label": "On the road",
"origin": "Airport",
"destination": "South Africa",
"message": "On the road from Airport to South Africa.",
"source": "operator_provided"
}
}09 / Download
Get report files and exports
Replace REPORT_ID with the returned id. For published scientific reports, format=pdf serves the TES-reviewed final PDF with narrative and all sample fields. source=samples remains a compact table preview for legacy archived reports, limited to eight columns; it is not the final scientific report. CSV and XLSX retain all sample fields. Downloads require a Bearer token.
curl -fL \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/download?format=pdf" \
-o report.pdf
curl -fL \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/download?format=pdf&source=samples" \
-o sample-results.pdf
curl -fL \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/download?format=csv" \
-o report.csv
curl -fL \
-H "Authorization: $TES_BEARER_TOKEN" \
"https://technicalengineeringsolutions.com/api/archive/share/reports/REPORT_ID/download?format=xlsx" \
-o report.xlsxEndpoint
GET /api/archive/share/reports/{reportId}/download
Authentication
Bearer token
Exports
PDF, CSV, XLSX