SGT SGT Systems Limited
Integration & API · API Reference · v1.0-preview

REST API Reference

Forward-looking reference for the SGT Systems REST API: authentication, endpoints, pagination, rate limits, errors, and code examples.

Updated May 24, 2026
15 views
Preview release. This document describes the forward-looking REST API surface for the SGT Systems platform. Endpoints and payloads listed here are stable in design but may evolve before the general-availability (GA) release. Full v1 GA is targeted for Q3. If you are integrating against an early-access deployment, please confirm the active version with your SGT Systems contact.

Overview

The SGT Systems REST API gives integrators a uniform, JSON-based interface to interact with managed IoT devices, telemetry streams, alarms, and reports across the platform. It is designed around standard HTTP semantics: predictable URLs, conventional verbs (GET, POST, PATCH, DELETE), and meaningful status codes. Every endpoint accepts and returns JSON encoded as UTF-8, and every response carries a unique request identifier for traceability.

The API is intended for three primary audiences:

  • Customer integrators pulling telemetry, raising work orders, or syncing devices into their own dashboards.
  • OEM partners embedding SGT Systems capabilities (alarming, reporting, edge orchestration) inside their own products.
  • Internal services within SGT Systems — automation jobs, the mobile field-service app, and analytics pipelines.

Throughout this reference, examples assume the production base URL and a Bearer-token API key. Sandbox and on-premises deployments follow the identical surface; only the hostname differs.

Authentication

All requests must be authenticated. The API supports two authentication mechanisms:

  1. API keys — sent as a bearer token in the Authorization header. Best for server-to-server integrations.
  2. OAuth2 (authorization code + PKCE) — for end-user, interactive flows, primarily from partner web apps and the SGT mobile app.
Authorization: Bearer sgt_live_5h7k9w...truncated
X-SGT-Tenant: acme-industries
Content-Type: application/json
Accept: application/json
User-Agent: acme-erp-sync/1.4.2

The X-SGT-Tenant header is required when an API key has multi-tenant scope. For single-tenant keys it may be omitted; if present it must match the key's bound tenant or the request is rejected with 403 forbidden. See the Authentication & API Keys guide for details on issuing, rotating, and scoping keys.

OAuth2 clients exchange an authorization code for an access token at https://auth.sgtsystems.com/oauth2/token. Access tokens are JWTs valid for one hour; refresh tokens are valid for 30 days of inactivity. Both flow types use the same Authorization: Bearer header on subsequent API calls — the API does not distinguish between the two on a per-request basis.

Base URL & Versioning

The production base URL is:

https://api.sgtsystems.com/v1

The sandbox URL is structurally identical:

https://api.sandbox.sgtsystems.com/v1

Versioning is URL-based. Breaking changes will only be introduced under a new major version (/v2, /v3, ...). Within a major version, the platform may add new optional fields, new endpoints, and new event types — clients must be tolerant of unknown fields. Deprecations are announced at least 180 days in advance via the developer changelog and a Deprecation response header with an RFC 9745 sunset date.

What counts as a breaking change on the SGT API:

  • Removing or renaming a field, endpoint, or scope.
  • Tightening a previously-accepted input (e.g. shortening a max length).
  • Changing the meaning or type of an existing field.

What is explicitly not a breaking change and may happen without a version bump:

  • Adding optional response fields.
  • Adding new endpoints.
  • Adding new event types or webhook payload fields.
  • Loosening input validation.

Common Response Formats

All successful responses return a JSON object with the following envelope:

{
  "data": { ... resource or array ... },
  "meta": {
    "request_id": "req_01HW3K9F2X...",
    "served_by":  "api-prod-us-east-2-a4",
    "took_ms":    37
  }
}

List endpoints additionally include a pagination object inside meta. Errors follow the structure described under Error Codes. Every response, success or error, includes the X-Request-Id header mirrored from meta.request_id — quote this value in any support request and the SGT operations team can usually trace the full lifecycle of the call in seconds.

Resource identifiers in responses use a stable prefix scheme so they are self-describing in logs:

PrefixResourceExample
dev_Devicedev_42
site_Site / locationsite_dhaka_wh1
alm_Alarm instancealm_01HW3K9F2X
evt_Eventevt_01HW3K9F2X
rpt_Report runrpt_01HW3K9F2X
wh_Webhook endpointwh_01HW3K9F2X
req_Requestreq_01HW3K9F2X

Pagination

List endpoints use cursor-based pagination. Cursors are opaque, URL-safe strings; do not parse them, do not store them long-term, and do not assume anything about their format — the platform reserves the right to change the encoding within a major version. Use the supplied next cursor verbatim.

GET /v1/devices?limit=100&cursor=eyJpZCI6Ijg5...

{
  "data": [ { ... }, { ... } ],
  "meta": {
    "pagination": {
      "limit":  100,
      "next":   "eyJpZCI6IjEwMC...",
      "has_more": true
    }
  }
}

Maximum limit is 500. When has_more is false there are no more pages. The platform deliberately does not expose a total count — for large result sets, computing an exact total would be prohibitively expensive. If you need a count, use a counting endpoint such as GET /devices/count which returns an approximation accurate to within ~1 minute of staleness.

Rate Limiting

Rate limits are enforced per API key, with separate buckets for read and write traffic:

BucketLimitWindowBurst
Read (GET, HEAD)600per minute120
Write (POST, PATCH, DELETE)120per minute30
Telemetry ingest10,000per minute2,000
Report runs30per hour5

Every response includes the following headers:

X-RateLimit-Limit:     600
X-RateLimit-Remaining: 572
X-RateLimit-Reset:     1716549600

When a limit is exceeded the API returns 429 Too Many Requests along with a Retry-After header indicating the number of seconds to wait. The bucket uses a leaky-bucket algorithm with the listed burst capacity — short spikes are absorbed, sustained over-rate traffic is rejected. Clients should implement exponential backoff with jitter, not a fixed-interval retry that will synchronise with other clients and prolong contention.

Higher limits. If your integration legitimately needs higher limits (large bulk migrations, large fleets), contact support before going live. We can raise individual keys; we cannot raise them retroactively after your job has already burned its budget.

Error Codes

Error responses use standard HTTP status codes and a consistent JSON body:

{
  "error": {
    "code":    "device_not_found",
    "message": "No device with id 'dev_42' exists in tenant 'acme-industries'.",
    "details": { "device_id": "dev_42" },
    "request_id": "req_01HW3K9F2X..."
  }
}
StatusCodeMeaningRetry?
400invalid_requestMalformed JSON or missing required fields.No
401unauthenticatedMissing or invalid credentials.No
403forbiddenAuthenticated, but key lacks the required scope.No
404not_foundResource does not exist or is not visible to the caller.No
409conflictState conflict, e.g. duplicate slug or stale If-Match.No (resolve first)
422validation_failedSemantic validation error; see details.No
429rate_limitedRate limit exceeded. Honor Retry-After.Yes, after delay
500internal_errorUnexpected server error; safe to retry with backoff.Yes, with backoff
502bad_gatewayUpstream component returned an unexpected response.Yes, with backoff
503unavailableTemporary outage; retry with exponential backoff.Yes, with backoff
504timeoutUpstream timeout; idempotent calls are safe to retry.Yes, idempotent only

Endpoints

Devices

Devices represent any field asset registered with the SGT platform — gateways, PLCs, energy meters, environmental sensors, and so on.

MethodPathPurpose
GET/devicesList devices, with filtering by site, type, and status.
POST/devicesRegister a new device.
GET/devices/{id}Fetch a single device.
PATCH/devices/{id}Update mutable fields (name, tags, site).
DELETE/devices/{id}Decommission a device. Soft-delete; data retained per retention policy.
POST/devices/{id}/commandsIssue an out-of-band command (reboot, refresh config).

Filterable query parameters on GET /devices: site, type, status (online, offline, provisioning), tag, updated_since (ISO-8601 timestamp). Filters are AND-combined. updated_since is the canonical way to build an incremental sync loop without re-reading the entire fleet on every poll.

Telemetry

Time-series readings from devices. Telemetry is append-only — corrections happen by writing a new reading with the same ts and a flag indicating supersession.

MethodPathPurpose
GET/telemetryQuery readings by device, metric, and time range.
POST/telemetry/ingestBulk ingest readings (up to 1,000 per request).
GET/telemetry/aggregateServer-side aggregation: avg, min, max, sum, p95, p99.
GET/telemetry/exportGenerate a signed CSV/Parquet export for a long range.

Time ranges are inclusive of from and exclusive of to. Maximum window per call is 31 days for raw readings, 366 days for aggregates. Larger windows return 400 invalid_request; use /telemetry/export instead.

Events & Alarms

MethodPathPurpose
GET/eventsList events, filterable by type, severity, and acknowledgement status.
POST/events/{id}/ackAcknowledge an alarm.
POST/events/{id}/clearManually clear an alarm.
POST/events/{id}/commentAttach an operator note for the audit trail.

Reports

MethodPathPurpose
POST/reportsTrigger an on-demand report run.
GET/reports/{id}Get report status and signed download URL once complete.
GET/reportsList recent runs for the tenant.

Reports are asynchronous. The initial POST returns 202 Accepted with a report ID; the response body indicates the expected completion time. Either poll GET /reports/{id} with a sensible backoff, or subscribe to the report.ready webhook event and wait for the push.

Webhook Subscriptions

MethodPathPurpose
GET/webhooksList your registered webhook endpoints.
POST/webhooksRegister a new endpoint; returns a generated signing secret.
PATCH/webhooks/{id}Update event subscriptions or rotate the secret.
DELETE/webhooks/{id}Unregister an endpoint.
POST/webhooks/{id}/testSend a signed test payload to verify your receiver.

For payload details and signature verification see Webhooks & Event Streaming.

Code Examples

curl — list devices

curl -sS https://api.sgtsystems.com/v1/devices?limit=20 \
  -H "Authorization: Bearer $SGT_API_KEY" \
  -H "X-SGT-Tenant: acme-industries" \
  -H "User-Agent: acme-erp-sync/1.4.2"

curl — ingest telemetry

curl -sS -X POST https://api.sgtsystems.com/v1/telemetry/ingest \
  -H "Authorization: Bearer $SGT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "readings": [
      {"device_id":"dev_42","metric":"power_kw","ts":"2026-05-24T10:00:00Z","value":12.7},
      {"device_id":"dev_42","metric":"power_kw","ts":"2026-05-24T10:00:05Z","value":12.9}
    ]
  }'

Python — query aggregated telemetry with retry

import os, time, random
import requests
from datetime import datetime, timedelta, timezone

API = "https://api.sgtsystems.com/v1"
headers = {
    "Authorization": f"Bearer {os.environ['SGT_API_KEY']}",
    "X-SGT-Tenant":  "acme-industries",
    "User-Agent":    "acme-erp-sync/1.4.2",
}

def get_with_retry(url, params, max_attempts=5):
    for attempt in range(max_attempts):
        r = requests.get(url, headers=headers, params=params, timeout=30)
        if r.status_code == 429:
            wait = int(r.headers.get("Retry-After", "5"))
            time.sleep(wait + random.uniform(0, 1))
            continue
        if 500 <= r.status_code < 600:
            time.sleep(min(60, 2 ** attempt) + random.uniform(0, 1))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError("exhausted retries")

end   = datetime.now(timezone.utc)
start = end - timedelta(hours=24)

resp = get_with_retry(
    f"{API}/telemetry/aggregate",
    params={
        "device_id": "dev_42",
        "metric":    "power_kw",
        "from":      start.isoformat(),
        "to":        end.isoformat(),
        "interval":  "15m",
        "fn":        "avg",
    },
)
for point in resp["data"]:
    print(point["ts"], point["value"])

Python — paginate through all devices

def iter_devices():
    cursor = None
    while True:
        params = {"limit": 200}
        if cursor:
            params["cursor"] = cursor
        page = get_with_retry(f"{API}/devices", params=params)
        for d in page["data"]:
            yield d
        pg = page["meta"]["pagination"]
        if not pg["has_more"]:
            break
        cursor = pg["next"]

for device in iter_devices():
    print(device["id"], device["name"], device["status"])
Tip. Always send a User-Agent identifying your integration (e.g. acme-erp-sync/1.4.2). It helps SGT Systems support staff quickly diagnose issues affecting a specific client and gives you a foothold for asking about the rollout of new API features.

Versioning of Resources

Mutable resources (devices, webhooks, alarm rules) carry an etag. To make a safe update, include the etag in an If-Match header. If the resource changed in the meantime, the server returns 409 conflict and you can re-read and retry. This prevents lost-update races between multiple concurrent integrators.

Need help?

Questions, edge cases, or feature requests? Reach the SGT Systems integrations team at support@sgtsystems.com or via the contact page. When opening a ticket, include the request_id from any failing response — it lets us trace the call end-to-end across our infrastructure.

v1.0-preview · Last updated May 24, 2026 · Published May 24, 2026
© 2026 Smart Global Tech Systems Limited
• Related documentation

More from Integration & API

Browse all →