Skip to content

API Reference

Axiospec Public API

Version 1.0.0

Contents

Overview

The Axiospec Public API lets you read and write your calibration program from your own systems: list and create instruments, log calibrations to the tamper-evident ledger, and read your sites and selected compliance standards.

Base URL: https://axiospec.com/api/public/v1

Authentication. Create a key in the app under Settings then API Keys (workspace admins only). Send it on every request as either Authorization: Bearer <key> or X-API-Key: <key>. Keys are prefixed ctk_.

Access. The API is available on the Professional and Scale plans. Keys on the Free or Starter plans receive 403 API_ACCESS_TIER_REQUIRED. A key acts as the person who created it. When that person is an auditor whose access end date has passed, the key receives 403 ACCESS_ENDED.

Scopes. A key has the read scope, or the write scope (which implies read). A read-only key that attempts a write receives 403 INSUFFICIENT_SCOPE.

Idempotency. Logging a calibration (POST /instruments/{id}/calibrations) requires an Idempotency-Key header. The ledger is append-only, so a retried request with the same key returns the original record instead of writing a duplicate. Creating an instrument (POST /instruments) accepts the same header as an option.

Pagination. List endpoints return {data: [...], pagination: {limit, offset, total, has_more}}. Page with limit and offset.

Errors. Errors are JSON with a machine-readable code, a human-readable message, and optional details (for example the missing_fields list on 422 FIELD_REQUIREMENTS_UNMET, or the field on 422 PASS_OVER_TOLERANCE_REASON_REQUIRED).

Passing over tolerance. Logging a PASS or PASS_WITH_ADJUSTMENT whose final reading falls outside nominal_value +/- tolerance requires a short written reason in out_of_tolerance_impact. The final reading is as_left_reading, or as_found_reading when no as-left was sent, so an instrument found out of tolerance and adjusted back into band needs nothing extra. Without the reason the write is rejected with 422 PASS_OVER_TOLERANCE_REASON_REQUIRED and details.field = out_of_tolerance_impact. Anything the server cannot compare is accepted: no nominal, no tolerance, no numeric reading, or readings whose units disagree.

Production
https://axiospec.com

Authentication

Every request needs an API key. Send it in either of these two ways.

Bearer token in the Authorization header

A per-workspace API key (prefixed ctk_) created under Settings then API Keys. Send it as Authorization: Bearer <key>.

curl "https://axiospec.com/api/public/v1/instruments" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

API key in the X-API-Key header

The same API key sent in the X-API-Key header instead of Authorization: Bearer.

curl "https://axiospec.com/api/public/v1/instruments" \
  -H "X-API-Key: $AXIOSPEC_API_KEY"

Instruments

List instruments (paginated) #

GET /api/public/v1/instruments

Query parameters

  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0
  • statusstring | null

    Filter by asset status (case-insensitive), for example in_service, out_of_service, OUT_FOR_CALIBRATION or REFERENCE_ONLY. A REFERENCE_ONLY instrument is kept on the register but never calibrated: it reports next_due_at null, is left out of the due worklist, and never matches a compliance_status filter.

    • At most 64 characters
  • site_idstring | null

    Filter to a single site (UUID).

  • location_idstring | null

    Filter to a single location (UUID).

  • updated_sincestring | null

    Incremental sync: only instruments modified at/after this ISO-8601 timestamp.

  • sortstring | null

    Sort order: one of created_at, -created_at, updated_at, -updated_at.

  • asset_tagstring | null

    Exact asset-tag lookup (resolve your key to our record).

    • At most 255 characters
  • serial_numberstring | null

    Exact serial-number lookup.

    • At most 255 characters
  • compliance_statusstring | null

    Filter by compliance token: NOT_CALIBRATED, COMPLIANT, WARNING, NON_COMPLIANT, or OUT_FOR_CALIBRATION. An unknown token returns 422. REFERENCE_ONLY instruments never match (they have no compliance state). The server works this filter out for each instrument, so when the other filters still leave a very large set, the request returns 422. Narrow first with status, location_id, site_id or updated_since.

    • At most 32 characters
  • next_due_beforestring | null

    Only instruments whose next calibration is due before this ISO date. Like compliance_status, this filter is worked out for each instrument, so a very large set returns 422. Narrow first with status, location_id, site_id or updated_since.

  • includestring | null

    Opt-in tombstones for delta sync. include=retired also returns retired (decommissioned) instruments, flagged deleted=true with a retired_at timestamp. A mirror then learns that an instrument was retired instead of seeing it drop silently from the active list. When omitted, only active instruments are returned.

Responses

  • 200 Successful Response

    application/json Page_InstrumentSummary_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "asset_tag": "string",
          "name": "string",
          "manufacturer": "string",
          "model": "string",
          "serial_number": "string",
          "status": "string",
          "is_quarantined": false,
          "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "location_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "compliance_status": "string",
          "last_calibration_at": "string",
          "next_due_at": "string",
          "retired_at": "string",
          "deleted": false,
          "due_extension": {
            "until": "string",
            "reason": "string",
            "extended_at": "string",
            "extended_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
          }
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/instruments" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Create an instrument #

POST /api/public/v1/instruments

Header parameters

  • Idempotency-Keystring | null

    Optional. A unique value you choose for this request, such as a UUID, up to 128 characters. A retried request with the same key returns the instrument the first request created instead of creating a duplicate. While the first request is still being processed, a retry returns 503; retry again shortly.

Request body Required

application/json InstrumentCreate

  • asset_tagstringRequired
    • At least 1 characters
    • At most 255 characters
  • namestringRequired
    • At least 1 characters
    • At most 255 characters
  • manufacturerstring | null
    • At most 255 characters
  • modelstring | null
    • At most 255 characters
  • serial_numberstring | null
    • At most 255 characters
  • categorystring | null
    • At most 255 characters
  • departmentstring | null
    • At most 255 characters
  • locationstring | null
    • At most 255 characters
  • tolerance_specstring | null
    • At most 255 characters
  • notesstring | null
    • At most 10000 characters
  • unit_of_measurestring | null
    • At most 64 characters
  • is_reference_standardboolean
    • Default: false
  • calibration_interval_valueintegerRequired
    • Minimum: 1
    • Maximum: 100000
  • calibration_interval_unitstring

    One of days, months, years.

    • Default: months
    • Allowed values: days, months, years
  • last_calibration_datestring (date-time) | null

    Optional baseline: when the instrument was last calibrated. When provided, an approved baseline record is created so compliance is computed from it. When omitted, the instrument reports NOT_CALIBRATED until its first calibration is logged.

  • customFieldsmap<string, string>

    Optional workspace-defined custom fields as a flat map of label -> value. Labels are stored exactly as sent. A blank value is dropped rather than stored.

Fields not listed here are rejected.

Example request body
{
  "asset_tag": "string",
  "name": "string",
  "manufacturer": "string",
  "model": "string",
  "serial_number": "string",
  "category": "string",
  "department": "string",
  "location": "string",
  "tolerance_spec": "string",
  "notes": "string",
  "unit_of_measure": "string",
  "is_reference_standard": false,
  "calibration_interval_value": 1,
  "calibration_interval_unit": "days",
  "last_calibration_date": "2026-01-15T14:30:00Z",
  "customFields": {
    "key": "string"
  }
}

Responses

  • 201 Successful Response

    application/json InstrumentDetail

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "asset_tag": "string",
      "name": "string",
      "manufacturer": "string",
      "model": "string",
      "serial_number": "string",
      "status": "string",
      "is_quarantined": false,
      "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "location_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "compliance_status": "string",
      "last_calibration_at": "string",
      "next_due_at": "string",
      "retired_at": "string",
      "deleted": false,
      "due_extension": {
        "until": "string",
        "reason": "string",
        "extended_at": "string",
        "extended_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      },
      "category": "string",
      "department": "string",
      "location": "string",
      "tolerance_spec": "string",
      "notes": "string",
      "unit_of_measure": "string",
      "is_reference_standard": false,
      "requires_electronic_signature": false,
      "requires_electronic_signature_override": true,
      "calibration_interval_value": 0,
      "calibration_interval_unit": "string",
      "grace_days": 0,
      "customFields": {
        "key": "string"
      },
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/instruments" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"asset_tag":"string","name":"string","calibration_interval_value":1}'

Get an instrument #

GET /api/public/v1/instruments/{instrument_id}

Path parameters

  • instrument_idstringRequired

Responses

  • 200 Successful Response

    application/json InstrumentDetail

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "asset_tag": "string",
      "name": "string",
      "manufacturer": "string",
      "model": "string",
      "serial_number": "string",
      "status": "string",
      "is_quarantined": false,
      "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "location_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "compliance_status": "string",
      "last_calibration_at": "string",
      "next_due_at": "string",
      "retired_at": "string",
      "deleted": false,
      "due_extension": {
        "until": "string",
        "reason": "string",
        "extended_at": "string",
        "extended_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      },
      "category": "string",
      "department": "string",
      "location": "string",
      "tolerance_spec": "string",
      "notes": "string",
      "unit_of_measure": "string",
      "is_reference_standard": false,
      "requires_electronic_signature": false,
      "requires_electronic_signature_override": true,
      "calibration_interval_value": 0,
      "calibration_interval_unit": "string",
      "grace_days": 0,
      "customFields": {
        "key": "string"
      },
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Update an instrument (partial) #

PATCH /api/public/v1/instruments/{instrument_id}

Path parameters

  • instrument_idstringRequired

Request body Required

application/json InstrumentUpdate

  • namestring | null
    • At least 1 characters
    • At most 255 characters
  • manufacturerstring | null
    • At most 255 characters
  • modelstring | null
    • At most 255 characters
  • serial_numberstring | null
    • At most 255 characters
  • categorystring | null
    • At most 255 characters
  • departmentstring | null
    • At most 255 characters
  • locationstring | null
    • At most 255 characters
  • tolerance_specstring | null
    • At most 255 characters
  • notesstring | null
    • At most 10000 characters
  • unit_of_measurestring | null
    • At most 64 characters
  • is_reference_standardboolean | null
  • calibration_interval_valueinteger | null
    • Minimum: 1
    • Maximum: 100000
  • calibration_interval_unitstring | null
    • Allowed values: days, months, years
  • calibration_interval_change_reasonstring | null

    Why the calibration interval is changing. Applies when the instrument already has an interval and the value or unit you send differs from it. The change is always written to the workspace activity log with this reason. Whether the reason is required depends on the deployment's CHANGE_REASON_RULES_ENFORCED setting: when it is on, the request fails with 422 and code interval_change_reason_required without one; when it is off, the change is accepted and logged with no reason. Send a reason now and your integration will not break when it turns on. Not needed when the interval is unchanged.

    • At most 1000 characters
  • customFieldsmap<string, string> | null

    Workspace-defined custom fields to set, as a flat map of label -> value. MERGES into the instrument's existing custom fields: labels you do not send are left alone, and sending a blank value removes that label. Omit the key entirely, or send null, to change nothing.

Fields not listed here are rejected.

Example request body
{
  "name": "string",
  "manufacturer": "string",
  "model": "string",
  "serial_number": "string",
  "category": "string",
  "department": "string",
  "location": "string",
  "tolerance_spec": "string",
  "notes": "string",
  "unit_of_measure": "string",
  "is_reference_standard": true,
  "calibration_interval_value": 1,
  "calibration_interval_unit": "days",
  "calibration_interval_change_reason": "string",
  "customFields": {
    "key": "string"
  }
}

Responses

  • 200 Successful Response

    application/json InstrumentDetail

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "asset_tag": "string",
      "name": "string",
      "manufacturer": "string",
      "model": "string",
      "serial_number": "string",
      "status": "string",
      "is_quarantined": false,
      "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "location_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "compliance_status": "string",
      "last_calibration_at": "string",
      "next_due_at": "string",
      "retired_at": "string",
      "deleted": false,
      "due_extension": {
        "until": "string",
        "reason": "string",
        "extended_at": "string",
        "extended_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      },
      "category": "string",
      "department": "string",
      "location": "string",
      "tolerance_spec": "string",
      "notes": "string",
      "unit_of_measure": "string",
      "is_reference_standard": false,
      "requires_electronic_signature": false,
      "requires_electronic_signature_override": true,
      "calibration_interval_value": 0,
      "calibration_interval_unit": "string",
      "grace_days": 0,
      "customFields": {
        "key": "string"
      },
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X PATCH "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"string"}'

Retire (decommission) an instrument #

POST /api/public/v1/instruments/{instrument_id}/retire

Record that an instrument has left active service.

The record stays. status becomes retired and retired_at is stamped. The instrument keeps its calibration history and stays readable through this API. The ledger is untouched. Nothing is deleted, and this API has no hard DELETE for an instrument.

Retiring stops new work on the instrument. After it, the write endpoints (update an instrument, log a calibration, upload a document) return 409 ASSET_RETIRED. Reads keep working.

The key needs the write scope, and its owner must be a workspace admin or manager. Any other key receives 403. A malformed instrument_id returns 422. An instrument that is not in your workspace returns 404.

Retiring is idempotent. Retiring an instrument that is already retired returns 200 with the same detail, and the original retired_at stands.

Path parameters

  • instrument_idstringRequired

Responses

  • 200 Successful Response

    application/json InstrumentDetail

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "asset_tag": "string",
      "name": "string",
      "manufacturer": "string",
      "model": "string",
      "serial_number": "string",
      "status": "string",
      "is_quarantined": false,
      "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "location_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "compliance_status": "string",
      "last_calibration_at": "string",
      "next_due_at": "string",
      "retired_at": "string",
      "deleted": false,
      "due_extension": {
        "until": "string",
        "reason": "string",
        "extended_at": "string",
        "extended_by_user_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      },
      "category": "string",
      "department": "string",
      "location": "string",
      "tolerance_spec": "string",
      "notes": "string",
      "unit_of_measure": "string",
      "is_reference_standard": false,
      "requires_electronic_signature": false,
      "requires_electronic_signature_override": true,
      "calibration_interval_value": 0,
      "calibration_interval_unit": "string",
      "grace_days": 0,
      "customFields": {
        "key": "string"
      },
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/retire" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Calibrations

List an instrument's calibration records (paginated) #

GET /api/public/v1/instruments/{instrument_id}/calibrations

Path parameters

  • instrument_idstringRequired

Query parameters

  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0
  • updated_sincestring | null

    Incremental sync: only records appended at/after this ISO-8601 timestamp.

  • performed_afterstring | null

    Only records performed at/after this ISO-8601 timestamp.

  • performed_beforestring | null

    Only records performed at/before this ISO-8601 timestamp.

  • resultstring | null

    Restrict to one or more calibration results (comma-separated, case-insensitive): one of PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED. An unknown token returns 422.

  • sortstring | null

    Sort order: one of performed_at, -performed_at, updated_at, -updated_at.

  • includestring | null

    Opt-in tombstones for delta sync. include=voided also returns void records (record_type='void', with voids_id pointing at the calibration it invalidates), so a mirror learns that a calibration was invalidated. When omitted, void records are left out.

Responses

  • 200 Successful Response

    application/json Page_CalibrationRecord_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "result": "string",
          "performed_at": "string",
          "performed_by": "string",
          "nominal_value": "string",
          "tolerance": "string",
          "as_found_reading": "string",
          "as_left_reading": "string",
          "reference_standard": "string",
          "certificate_number": "string",
          "traceability_reference": "string",
          "service_provider": "string",
          "measurement_uncertainty": "string",
          "coverage_factor": "string",
          "confidence_level": "string",
          "decision_rule": "string",
          "conformity_statement": "string",
          "notes": "string",
          "calibration_type": "in_house",
          "approval_status": "string",
          "status": "string",
          "superseded_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "record_type": "calibration",
          "voids_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "voided_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "created_at": "string"
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/calibrations" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Log a calibration for an instrument #

POST /api/public/v1/instruments/{instrument_id}/calibrations

Path parameters

  • instrument_idstringRequired

Header parameters

  • Idempotency-Keystring | nullRequired

    Required. A unique value you choose for this calibration, such as a UUID, up to 128 characters. The ledger is append-only, so a retried request with the same key returns the original record instead of writing a duplicate. Use a new key for each new calibration. A request without the header returns 400 IDEMPOTENCY_KEY_REQUIRED. While the first request is still being processed, a retry returns 503; retry again shortly.

Request body Required

application/json CalibrationLogRequest

  • resultstringRequired

    One of PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED.

    • Allowed values: PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED
  • performed_atstring (date-time) | null

    When the calibration was performed. Defaults to now (UTC) if omitted.

  • nominal_valuestring | null
    • At most 255 characters
  • tolerancestring | null
    • At most 255 characters
  • as_found_readingstring | null
    • At most 255 characters
  • as_left_readingstring | null
    • At most 255 characters
  • temperaturestring | null
    • At most 64 characters
  • humiditystring | null
    • At most 64 characters
  • reference_standardstring | null
    • At most 255 characters
  • reference_standard_asset_idstring (uuid) | null
  • certificate_numberstring | null
    • At most 255 characters
  • traceability_referencestring | null
    • At most 255 characters
  • service_providerstring | null
    • At most 255 characters
  • calibration_typestring | null

    'in_house' when your team measured the instrument, 'external_certificate' when a lab or vendor calibrated it and you are recording their certificate. On the external path the floor is result, performed_at and certificate_number; the reading fields are not required.

    • Allowed values: in_house, external_certificate
  • measurement_uncertaintystring | null
    • At most 120 characters
  • coverage_factorstring | null
    • At most 40 characters
  • confidence_levelstring | null
    • At most 40 characters
  • decision_rulestring | null
    • At most 120 characters
  • conformity_statementstring | null
    • At most 4000 characters
  • reference_standard_certificate_numberstring | null
    • At most 120 characters
  • restriction_notesstring | null
    • At most 4000 characters
  • out_of_tolerance_impactstring | null
    • At most 4000 characters
  • notesstring | null
    • At most 4000 characters

Fields not listed here are rejected.

Example request body
{
  "result": "PASS",
  "performed_at": "2026-01-15T14:30:00Z",
  "nominal_value": "string",
  "tolerance": "string",
  "as_found_reading": "string",
  "as_left_reading": "string",
  "temperature": "string",
  "humidity": "string",
  "reference_standard": "string",
  "reference_standard_asset_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "certificate_number": "string",
  "traceability_reference": "string",
  "service_provider": "string",
  "calibration_type": "in_house",
  "measurement_uncertainty": "string",
  "coverage_factor": "string",
  "confidence_level": "string",
  "decision_rule": "string",
  "conformity_statement": "string",
  "reference_standard_certificate_number": "string",
  "restriction_notes": "string",
  "out_of_tolerance_impact": "string",
  "notes": "string"
}

Responses

  • 201 Successful Response

    application/json CalibrationRecord

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "result": "string",
      "performed_at": "string",
      "performed_by": "string",
      "nominal_value": "string",
      "tolerance": "string",
      "as_found_reading": "string",
      "as_left_reading": "string",
      "reference_standard": "string",
      "certificate_number": "string",
      "traceability_reference": "string",
      "service_provider": "string",
      "measurement_uncertainty": "string",
      "coverage_factor": "string",
      "confidence_level": "string",
      "decision_rule": "string",
      "conformity_statement": "string",
      "notes": "string",
      "calibration_type": "in_house",
      "approval_status": "string",
      "status": "string",
      "superseded_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "record_type": "calibration",
      "voids_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "voided_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "created_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/calibrations" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"result":"PASS"}'

List all calibration records across the workspace #

GET /api/public/v1/calibrations

Every calibration record in your workspace, across all instruments, in one paginated feed. Use it for a warehouse or BI job that pulls "all calibrations since X" without making one call per instrument.

For incremental sync, pass updated_since with sort=updated_at and page through with limit and offset. Add include=voided to also receive void records, so a mirror can drop calibrations that were invalidated.

Query parameters

  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0
  • updated_sincestring | null

    Incremental sync: only records appended at/after this ISO-8601 timestamp.

  • performed_afterstring | null

    Only records performed at/after this ISO-8601 timestamp.

  • performed_beforestring | null

    Only records performed at/before this ISO-8601 timestamp.

  • instrument_idstring | null

    Restrict the feed to a single instrument (UUID).

  • resultstring | null

    Restrict to one or more calibration results (comma-separated, case-insensitive): one of PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED. An unknown token returns 422.

  • sortstring | null

    Sort order: one of performed_at, -performed_at, updated_at, -updated_at.

  • includestring | null

    Opt-in tombstones for delta sync. include=voided also returns void records (record_type='void', with voids_id pointing at the calibration it invalidates) across the whole workspace. This is how a full mirror learns that calibrations were invalidated. When omitted, void records are left out.

Responses

  • 200 Successful Response

    application/json Page_CalibrationRecord_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "result": "string",
          "performed_at": "string",
          "performed_by": "string",
          "nominal_value": "string",
          "tolerance": "string",
          "as_found_reading": "string",
          "as_left_reading": "string",
          "reference_standard": "string",
          "certificate_number": "string",
          "traceability_reference": "string",
          "service_provider": "string",
          "measurement_uncertainty": "string",
          "coverage_factor": "string",
          "confidence_level": "string",
          "decision_rule": "string",
          "conformity_statement": "string",
          "notes": "string",
          "calibration_type": "in_house",
          "approval_status": "string",
          "status": "string",
          "superseded_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "record_type": "calibration",
          "voids_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "voided_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "created_at": "string"
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/calibrations" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Get a single calibration record #

GET /api/public/v1/calibrations/{calibration_id}

Path parameters

  • calibration_idstringRequired

Responses

  • 200 Successful Response

    application/json CalibrationRecord

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "result": "string",
      "performed_at": "string",
      "performed_by": "string",
      "nominal_value": "string",
      "tolerance": "string",
      "as_found_reading": "string",
      "as_left_reading": "string",
      "reference_standard": "string",
      "certificate_number": "string",
      "traceability_reference": "string",
      "service_provider": "string",
      "measurement_uncertainty": "string",
      "coverage_factor": "string",
      "confidence_level": "string",
      "decision_rule": "string",
      "conformity_statement": "string",
      "notes": "string",
      "calibration_type": "in_house",
      "approval_status": "string",
      "status": "string",
      "superseded_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "record_type": "calibration",
      "voids_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "voided_by_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "created_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/calibrations/CALIBRATION_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Download the calibration certificate PDF for a calibration record #

GET /api/public/v1/calibrations/{calibration_id}/certificate

Download the certificate for one calibration record as a PDF (application/pdf, sent as an attachment).

This is the same certificate the app issues for the record. It is stored the first time it is generated, so later downloads return the same document unless the certificate is reissued in the app. A change to the workspace's document language also reissues it: the next download returns it in the new language. Certificates stay available after the instrument is retired.

Errors: 422 when calibration_id is not a UUID. 404 when the record is not in your workspace or is not a calibration. 404 CERTIFICATE_UNAVAILABLE when the record exists but cannot be certified because it was voided, was superseded by a correction, or is not approved.

Path parameters

  • calibration_idstringRequired

Responses

  • 200 PDF certificate

    application/pdf

  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/calibrations/CALIBRATION_ID/certificate" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -o certificate.pdf

Documents

Get a presigned URL to upload a document #

POST /api/public/v1/instruments/{instrument_id}/documents/upload-url

Path parameters

  • instrument_idstringRequired

Request body Required

application/json DocumentUploadUrlRequest

  • file_namestringRequired
    • At least 1 characters
    • At most 255 characters
  • content_typestring

    MIME type of the file. Must be an allow-listed type (PDF or an image); active content such as text/html or image/svg+xml is rejected.

    • Default: application/octet-stream
    • At most 255 characters
  • size_bytesinteger | null

    Optional declared file size. Rejected (422) if it exceeds the maximum; the presigned POST also caps the actual upload at S3.

    • Minimum: 1
    • Maximum: 26214400

Fields not listed here are rejected.

Example request body
{
  "file_name": "string",
  "content_type": "application/octet-stream",
  "size_bytes": 1
}

Responses

  • 200 Successful Response

    application/json DocumentUploadUrlResponse

    Example response
    {
      "method": "POST",
      "upload_url": "string",
      "fields": {
        "key": "string"
      },
      "key": "string",
      "expires_in": 0,
      "max_size_bytes": 0
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/documents/upload-url" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"file_name":"string"}'

List an instrument's documents #

GET /api/public/v1/instruments/{instrument_id}/documents

Path parameters

  • instrument_idstringRequired

Query parameters

  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0
  • calibration_idstring | null

    Filter to one calibration's documents (UUID).

  • document_typestring | null

    Filter to one document type (one of certificate, report, procedure, photo, other).

  • uploaded_sincestring | null

    Incremental sync: only documents uploaded at/after this ISO-8601 timestamp. Note this is a NEW-upload cursor keyed on upload_date and does not reflect later archive/soft-delete changes.

  • sortstring | null

    Sort order: upload_date or -upload_date (default: -upload_date).

  • include_archivedboolean

    Include archived documents (default: active only).

    • Default: false

Responses

  • 200 Successful Response

    application/json Page_DocumentRecord_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "file_name": "string",
          "document_type": "string",
          "calibration_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "uploaded_at": "string",
          "uploaded_by": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "is_archived": false
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/documents" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Register an uploaded document #

POST /api/public/v1/instruments/{instrument_id}/documents

Path parameters

  • instrument_idstringRequired

Request body Required

application/json DocumentRegisterRequest

  • keystringRequired
    • At least 1 characters
    • At most 512 characters
  • file_namestringRequired
    • At least 1 characters
    • At most 255 characters
  • document_typestring

    One of certificate, report, procedure, photo, other.

    • Default: other
    • Allowed values: certificate, report, procedure, photo, other
  • calibration_idstring (uuid) | null

    Optional: attach the document to a specific calibration record on THIS instrument. Must be a calibration id belonging to this instrument.

Fields not listed here are rejected.

Example request body
{
  "key": "string",
  "file_name": "string",
  "document_type": "certificate",
  "calibration_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

Responses

  • 201 Successful Response

    application/json DocumentRecord

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "instrument_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "file_name": "string",
      "document_type": "string",
      "calibration_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "uploaded_at": "string",
      "uploaded_by": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "is_archived": false
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/documents" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"string","file_name":"string"}'

Get a short-lived download URL for a document #

GET /api/public/v1/instruments/{instrument_id}/documents/{document_id}/download

Path parameters

  • instrument_idstringRequired
  • document_idstring (uuid)Required

Responses

Example request

curl "https://axiospec.com/api/public/v1/instruments/INSTRUMENT_ID/documents/DOCUMENT_ID/download" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Due worklist

List due / overdue instruments (worklist) #

GET /api/public/v1/due

Query parameters

  • horizon_daysinteger

    Future window in days (1-365). Everything due within this many days is included; the OVERDUE backlog is ALWAYS included regardless of it.

    • Default: 30
    • Minimum: 1
    • Maximum: 365
  • site_idstring | null

    Filter to a single site (UUID). Intersects your site scope.

  • statusstring | null

    Filter by compliance token (case-insensitive): NOT_CALIBRATED, COMPLIANT, WARNING, NON_COMPLIANT, or OUT_FOR_CALIBRATION.

    • At most 32 characters
  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0

Responses

  • 200 Successful Response

    application/json Page_DueInstrument_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "asset_tag": "string",
          "name": "string",
          "due_at": "string",
          "due_date": "string",
          "compliance_status": "string",
          "interval_label": "string",
          "site_name": "string",
          "location_name": "string"
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/due" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Webhooks

List the webhook event catalog #

GET /api/public/v1/webhooks/events

The machine-readable v1 event vocabulary an endpoint can subscribe to.

Responses

  • 200 Successful Response

    application/json EventCatalogResponse

    Example response
    {
      "events": [
        {
          "type": "string",
          "description": "string"
        }
      ]
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/webhooks/events" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

List webhook endpoints #

GET /api/public/v1/webhooks

Query parameters

  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0

Responses

  • 200 Successful Response

    application/json Page_WebhookRead_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "url": "string",
          "events": [
            "string"
          ],
          "description": "string",
          "is_active": true,
          "secret_prefix": "string",
          "disabled_at": "string",
          "created_at": "string",
          "updated_at": "string"
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Subscribe a webhook endpoint #

POST /api/public/v1/webhooks

Request body Required

application/json WebhookCreate

  • urlstringRequired

    HTTPS delivery URL. Validated for SSRF (must resolve to a public address; private/loopback/link-local/metadata targets are rejected).

    • At most 2048 characters
  • eventsarray[string] | null

    Subset of the v1 event catalog to receive. Omit or send an empty list to subscribe to ALL events. Unknown types are rejected (422).

  • descriptionstring | null

    Optional human-readable label for this endpoint.

    • At most 500 characters

Fields not listed here are rejected.

Example request body
{
  "url": "string",
  "events": [
    "string"
  ],
  "description": "string"
}

Responses

  • 201 Successful Response

    application/json WebhookCreatedResponse

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "url": "string",
      "events": [
        "string"
      ],
      "description": "string",
      "is_active": true,
      "secret_prefix": "string",
      "disabled_at": "string",
      "created_at": "string",
      "updated_at": "string",
      "secret": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/webhooks" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"string"}'

Get a webhook endpoint #

GET /api/public/v1/webhooks/{endpoint_id}

Path parameters

  • endpoint_idstring (uuid)Required

Responses

  • 200 Successful Response

    application/json WebhookRead

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "url": "string",
      "events": [
        "string"
      ],
      "description": "string",
      "is_active": true,
      "secret_prefix": "string",
      "disabled_at": "string",
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Update a webhook endpoint #

PATCH /api/public/v1/webhooks/{endpoint_id}

Path parameters

  • endpoint_idstring (uuid)Required

Request body Required

application/json WebhookUpdate

  • urlstring | null

    New HTTPS delivery URL (re-validated for SSRF when changed).

    • At most 2048 characters
  • eventsarray[string] | null

    Replacement event subset (empty/null = all events).

  • descriptionstring | null

    Replacement label.

    • At most 500 characters
  • is_activeboolean | null

    Enable (true) or disable (false) delivery to this endpoint.

Fields not listed here are rejected.

Example request body
{
  "url": "string",
  "events": [
    "string"
  ],
  "description": "string",
  "is_active": true
}

Responses

  • 200 Successful Response

    application/json WebhookRead

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "url": "string",
      "events": [
        "string"
      ],
      "description": "string",
      "is_active": true,
      "secret_prefix": "string",
      "disabled_at": "string",
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X PATCH "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"string"}'

Unsubscribe (soft-disable) a webhook endpoint #

DELETE /api/public/v1/webhooks/{endpoint_id}

Path parameters

  • endpoint_idstring (uuid)Required

Responses

Example request

curl -X DELETE "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Rotate a webhook endpoint's signing secret #

POST /api/public/v1/webhooks/{endpoint_id}/rotate-secret

Path parameters

  • endpoint_idstring (uuid)Required

Responses

  • 200 Successful Response

    application/json SecretRotatedResponse

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "secret": "string",
      "secret_prefix": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID/rotate-secret" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

List an endpoint's delivery log #

GET /api/public/v1/webhooks/{endpoint_id}/deliveries

Path parameters

  • endpoint_idstring (uuid)Required

Query parameters

  • statusstring | null

    Filter by delivery status: pending, failed, succeeded, exhausted.

    • At most 32 characters
  • limitinteger
    • Default: 50
    • Minimum: 1
    • Maximum: 100
  • offsetinteger
    • Default: 0
    • Minimum: 0

Responses

  • 200 Successful Response

    application/json Page_WebhookDeliveryRead_

    Example response
    {
      "data": [
        {
          "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "event_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
          "event_type": "string",
          "status": "string",
          "attempt_count": 0,
          "next_attempt_at": "string",
          "last_attempt_at": "string",
          "last_status_code": 0,
          "last_error": "string",
          "response_snippet": "string",
          "created_at": "string",
          "updated_at": "string"
        }
      ],
      "pagination": {
        "limit": 0,
        "offset": 0,
        "total": 0,
        "has_more": true
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID/deliveries" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Manually replay a delivery #

POST /api/public/v1/webhooks/{endpoint_id}/deliveries/{delivery_id}/retry

Path parameters

  • endpoint_idstring (uuid)Required
  • delivery_idstring (uuid)Required

Responses

  • 202 Successful Response

    application/json WebhookDeliveryRead

    Example response
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "event_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "event_type": "string",
      "status": "string",
      "attempt_count": 0,
      "next_attempt_at": "string",
      "last_attempt_at": "string",
      "last_status_code": 0,
      "last_error": "string",
      "response_snippet": "string",
      "created_at": "string",
      "updated_at": "string"
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl -X POST "https://axiospec.com/api/public/v1/webhooks/ENDPOINT_ID/deliveries/DELIVERY_ID/retry" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Sites

List the workspace's sites #

GET /api/public/v1/sites

Responses

  • 200 Successful Response

    application/json array[SiteSummary]

    Example response
    [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "string",
        "is_default": false,
        "timezone": "string"
      }
    ]
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/sites" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Locations

List the workspace's locations #

GET /api/public/v1/locations

Query parameters

  • site_idstring | null

    Filter to a single site (UUID). Intersects your site scope.

Responses

  • 200 Successful Response

    application/json array[LocationSummary]

    Example response
    [
      {
        "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "name": "string",
        "site_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
      }
    ]
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/locations" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Compliance standards

List the compliance standards this workspace has selected #

GET /api/public/v1/standards

Responses

  • 200 Successful Response

    application/json array[StandardSummary]

    Example response
    [
      {
        "key": "string",
        "label": "string"
      }
    ]
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/standards" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

The standard-driven field requirements for a calibration/instrument #

GET /api/public/v1/standards/field-requirements

Query parameters

  • standardsstring | null

    Optional comma-separated standard keys to PREVIEW (e.g. 'iso_17025,as9100'). Omit to reflect the workspace's selected standards (what the public calibration POST is actually judged against). An unknown key returns 422.

Responses

  • 200 Successful Response

    application/json FieldRequirementsResponse

    Example response
    {
      "version": 0,
      "source": "workspace",
      "standards": [
        {
          "key": "string",
          "label": "string"
        }
      ],
      "available_standards": [
        {
          "key": "string",
          "label": "string"
        }
      ],
      "calibration": {
        "enforced": true,
        "fields": [
          {
            "field": "string",
            "label": "string",
            "requirement": "string",
            "required_by": [
              "string"
            ],
            "request_field": "string",
            "note": "string"
          }
        ]
      },
      "asset": {
        "enforced": true,
        "fields": [
          {
            "field": "string",
            "label": "string",
            "requirement": "string",
            "required_by": [
              "string"
            ],
            "request_field": "string",
            "note": "string"
          }
        ]
      }
    }
  • 422 Validation Error

    application/json HTTPValidationError

Example request

curl "https://axiospec.com/api/public/v1/standards/field-requirements" \
  -H "Authorization: Bearer $AXIOSPEC_API_KEY"

Schemas

The objects the endpoints accept and return. Field names, types and enum values are shown exactly as the API sends and expects them.

CalibrationLogRequest #

Request body for logging a calibration. Unknown keys are rejected (422).

The record is appended to the tamper-evident ledger with the same validation and approval rules as a calibration logged in the app. A FAIL or DAMAGED result quarantines the instrument, as it does in the app. The fields your workspace's selected standards require are enforced: a body that leaves one out is rejected with 422 FIELD_REQUIREMENTS_UNMET. Check them first with GET /standards/field-requirements.

A PASS or PASS_WITH_ADJUSTMENT whose final reading sits outside nominal_value +/- tolerance also needs a short written reason in out_of_tolerance_impact, or the write is rejected with 422 PASS_OVER_TOLERANCE_REASON_REQUIRED. The final reading is as_left_reading, or as_found_reading when no as-left was sent, so an instrument found out of tolerance and adjusted back into band needs nothing extra. Anything the server cannot compare is accepted.

Fields

  • resultstringRequired

    One of PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED.

    • Allowed values: PASS, PASS_WITH_ADJUSTMENT, LIMITED_CALIBRATION, FAIL, DAMAGED
  • performed_atstring (date-time) | null

    When the calibration was performed. Defaults to now (UTC) if omitted.

  • nominal_valuestring | null
    • At most 255 characters
  • tolerancestring | null
    • At most 255 characters
  • as_found_readingstring | null
    • At most 255 characters
  • as_left_readingstring | null
    • At most 255 characters
  • temperaturestring | null
    • At most 64 characters
  • humiditystring | null
    • At most 64 characters
  • reference_standardstring | null
    • At most 255 characters
  • reference_standard_asset_idstring (uuid) | null
  • certificate_numberstring | null
    • At most 255 characters
  • traceability_referencestring | null
    • At most 255 characters
  • service_providerstring | null
    • At most 255 characters
  • calibration_typestring | null

    'in_house' when your team measured the instrument, 'external_certificate' when a lab or vendor calibrated it and you are recording their certificate. On the external path the floor is result, performed_at and certificate_number; the reading fields are not required.

    • Allowed values: in_house, external_certificate
  • measurement_uncertaintystring | null
    • At most 120 characters
  • coverage_factorstring | null
    • At most 40 characters
  • confidence_levelstring | null
    • At most 40 characters
  • decision_rulestring | null
    • At most 120 characters
  • conformity_statementstring | null
    • At most 4000 characters
  • reference_standard_certificate_numberstring | null
    • At most 120 characters
  • restriction_notesstring | null
    • At most 4000 characters
  • out_of_tolerance_impactstring | null
    • At most 4000 characters
  • notesstring | null
    • At most 4000 characters

Fields not listed here are rejected.

CalibrationRecord #

A calibration record from the ledger, read-only. Ledger records are never edited or deleted: a correction or a void is appended as a new record.

Fields

  • idstring (uuid)Required
  • instrument_idstring (uuid)Required
  • resultstring | null
  • performed_atstring | null
  • performed_bystring | null
  • nominal_valuestring | null
  • tolerancestring | null
  • as_found_readingstring | null
  • as_left_readingstring | null
  • reference_standardstring | null
  • certificate_numberstring | null
  • traceability_referencestring | null
  • service_providerstring | null
  • measurement_uncertaintystring | null
  • coverage_factorstring | null
  • confidence_levelstring | null
  • decision_rulestring | null
  • conformity_statementstring | null
  • notesstring | null
  • calibration_typestring | null

    'in_house' or 'external_certificate' as recorded. Null when the record carries no type.

    • Allowed values: in_house, external_certificate
  • approval_statusstring | null
  • statusstring | null
  • superseded_by_idstring (uuid) | null
  • record_typestring

    'calibration' for a normal record, or 'void' for an appended void tombstone (surfaced via ?include=voided).

    • Default: calibration
    • Allowed values: calibration, void
  • voids_idstring (uuid) | null

    On a void tombstone (record_type='void'): the id of the calibration record this void invalidates. Null on a normal record.

  • voided_by_idstring (uuid) | null

    On a still-visible original calibration that has since been voided: the id of the void tombstone that invalidated it. Null when the record is not voided.

  • created_atstring | null

DocumentDownloadResponse #

A short-lived download URL for a document. The URL always downloads the file rather than displaying it in a browser, and it expires after expires_in seconds (at expires_at). Request a new URL each time you need the file instead of storing one.

Fields

  • download_urlstringRequired
  • expires_inintegerRequired
  • expires_atstringRequired

DocumentRecord #

A document attached to an instrument, read-only. Documents are identified by their id. To fetch the file, request a short-lived URL from GET /instruments/{instrument_id}/documents/{document_id}/download.

Fields

  • idstring (uuid)Required
  • instrument_idstring (uuid)Required
  • file_namestring | null
  • document_typestring | null
  • calibration_idstring (uuid) | null

    The calibration record this document is attached to, or null.

  • uploaded_atstring | null
  • uploaded_bystring (uuid) | null
  • is_archivedboolean
    • Default: false

DocumentRegisterRequest #

Request body for registering a file you uploaded with the presigned POST as a document on this instrument, optionally attached to one of its calibration records.

key must be the exact key the upload-url call returned for this instrument. Any other key is rejected (422 INVALID_REQUEST), and a key with no uploaded file returns 409 OBJECT_NOT_UPLOADED.

Fields

  • keystringRequired
    • At least 1 characters
    • At most 512 characters
  • file_namestringRequired
    • At least 1 characters
    • At most 255 characters
  • document_typestring

    One of certificate, report, procedure, photo, other.

    • Default: other
    • Allowed values: certificate, report, procedure, photo, other
  • calibration_idstring (uuid) | null

    Optional: attach the document to a specific calibration record on THIS instrument. Must be a calibration id belonging to this instrument.

Fields not listed here are rejected.

DocumentUploadUrlRequest #

Request body for an upload URL: a short-lived, size-capped presigned S3 POST for one document.

The server builds the object key; you cannot choose it. Any directory part of file_name is dropped.

Fields

  • file_namestringRequired
    • At least 1 characters
    • At most 255 characters
  • content_typestring

    MIME type of the file. Must be an allow-listed type (PDF or an image); active content such as text/html or image/svg+xml is rejected.

    • Default: application/octet-stream
    • At most 255 characters
  • size_bytesinteger | null

    Optional declared file size. Rejected (422) if it exceeds the maximum; the presigned POST also caps the actual upload at S3.

    • Minimum: 1
    • Maximum: 26214400

Fields not listed here are rejected.

DocumentUploadUrlResponse #

A presigned S3 POST for uploading the file. Send a multipart/form-data POST to upload_url with every entry in fields, then the file itself. After the upload, pass key to POST /instruments/{instrument_id}/documents to register the document.

Fields

  • methodstring
    • Default: POST
    • Always POST
  • upload_urlstringRequired
  • fieldsmap<string, string>Required
  • keystringRequired
  • expires_inintegerRequired
  • max_size_bytesintegerRequired

DueExtension #

Fields

  • untilstringRequired
  • reasonstring | null
  • extended_atstring | null
  • extended_by_user_idstring (uuid) | null

DueInstrument #

One worklist row: an instrument that comes due within the requested horizon, or is already overdue.

compliance_status is one of COMPLIANT, WARNING, NON_COMPLIANT or NOT_CALIBRATED, the same values the instrument endpoints return. It can also be OUT_FOR_CALIBRATION while the instrument is out at a vendor, a value the instrument endpoints do not return in compliance_status.

due_date is YYYY-MM-DD in the instrument's site timezone. Group rows by this value rather than deriving a day from due_at in your own timezone. due_at is the full ISO-8601 instant in UTC. An instrument that has an active calibration plan but has never been calibrated appears as due now, with NOT_CALIBRATED.

Fields

  • idstring (uuid)Required
  • asset_tagstring | null
  • namestring | null
  • due_atstring | null
  • due_datestringRequired
  • compliance_statusstringRequired
  • interval_labelstring | null
  • site_namestring | null
  • location_namestring | null

EventCatalogEntry #

One machine-readable event-type descriptor for integrator discovery.

Fields

  • typestringRequired
  • descriptionstringRequired

EventCatalogResponse #

The v1 event catalog: the full set of type values a webhook can carry.

Fields

FieldRequirement #

How a single standard-driven field is treated for the resolved standards.

requirement is the strictest-wins level across the resolved standards (required > recommended > optional > hidden). required_by lists the labels of the resolved standards that make it REQUIRED (empty when only the base floor requires it); it matches required_by in the missing_fields of a 422 FIELD_REQUIREMENTS_UNMET error. request_field (calibration scope only) is the POST body key that satisfies the field, or null when it is set server-side or not settable via the public API; note explains such cases.

Fields

  • fieldstringRequired
  • labelstringRequired
  • requirementstringRequired
  • required_byarray[string]
  • request_fieldstring | null
  • notestring | null

FieldRequirementsResponse #

The machine-readable, standard-driven field requirements. Use them to check a calibration (or instrument) write before you send it, instead of waiting for the server to reject it.

source is workspace when the requirements reflect the workspace's selected standards, or query when they preview an explicit ?standards= set. available_standards is the full selectable catalog (for discovering valid ?standards= keys).

Fields

FieldRequirementsScope #

The field requirements for one scope (calibration or asset).

enforced says whether the server rejects a write that leaves out a REQUIRED field. Logging a calibration does (422 FIELD_REQUIREMENTS_UNMET). POST /instruments does not: asset requirements are advisory only, so do not rely on a rejection there.

Fields

InstrumentCreate #

Request body for creating an instrument with POST /instruments. Unknown keys are rejected (422). The calibration interval is required because it starts the instrument's calibration schedule.

Fields

  • asset_tagstringRequired
    • At least 1 characters
    • At most 255 characters
  • namestringRequired
    • At least 1 characters
    • At most 255 characters
  • manufacturerstring | null
    • At most 255 characters
  • modelstring | null
    • At most 255 characters
  • serial_numberstring | null
    • At most 255 characters
  • categorystring | null
    • At most 255 characters
  • departmentstring | null
    • At most 255 characters
  • locationstring | null
    • At most 255 characters
  • tolerance_specstring | null
    • At most 255 characters
  • notesstring | null
    • At most 10000 characters
  • unit_of_measurestring | null
    • At most 64 characters
  • is_reference_standardboolean
    • Default: false
  • calibration_interval_valueintegerRequired
    • Minimum: 1
    • Maximum: 100000
  • calibration_interval_unitstring

    One of days, months, years.

    • Default: months
    • Allowed values: days, months, years
  • last_calibration_datestring (date-time) | null

    Optional baseline: when the instrument was last calibrated. When provided, an approved baseline record is created so compliance is computed from it. When omitted, the instrument reports NOT_CALIBRATED until its first calibration is logged.

  • customFieldsmap<string, string>

    Optional workspace-defined custom fields as a flat map of label -> value. Labels are stored exactly as sent. A blank value is dropped rather than stored.

Fields not listed here are rejected.

InstrumentDetail #

A single instrument: everything in the summary plus its descriptive and scheduling fields.

Fields

  • idstring (uuid)Required
  • asset_tagstring | null
  • namestring | null
  • manufacturerstring | null
  • modelstring | null
  • serial_numberstring | null
  • statusstring | null
  • is_quarantinedboolean
    • Default: false
  • site_idstring (uuid) | null
  • location_idstring (uuid) | null
  • compliance_statusstring | null
  • last_calibration_atstring | null
  • next_due_atstring | null
  • retired_atstring | null

    Tombstone marker: the ISO-8601 instant this instrument was retired (decommissioned), or null while active. Populated on retired rows surfaced via ?include=retired.

  • deletedboolean

    Tombstone marker: true when this instrument has been retired and should be treated as removed by a mirror. Always false on the default active-only list.

    • Default: false
  • due_extensionDueExtension | null

    The active due-date extension (until, reason, extended_at, extended_by_user_id), or null when there is none or a newer calibration has superseded it. next_due_at already reflects it.

  • categorystring | null
  • departmentstring | null
  • locationstring | null
  • tolerance_specstring | null
  • notesstring | null
  • unit_of_measurestring | null
  • is_reference_standardboolean
    • Default: false
  • requires_electronic_signatureboolean
    • Default: false
  • requires_electronic_signature_overrideboolean | null
  • calibration_interval_valueinteger | null
  • calibration_interval_unitstring | null
  • grace_daysinteger | null
  • customFieldsmap<string, string>

    Workspace-defined custom fields as a flat map of label -> value. Returned on the single-instrument detail only (not on the list).

  • created_atstring | null
  • updated_atstring | null

InstrumentSummary #

An instrument as it appears in a list: its identity and compliance state, enough to show a row without a second request.

Fields

  • idstring (uuid)Required
  • asset_tagstring | null
  • namestring | null
  • manufacturerstring | null
  • modelstring | null
  • serial_numberstring | null
  • statusstring | null
  • is_quarantinedboolean
    • Default: false
  • site_idstring (uuid) | null
  • location_idstring (uuid) | null
  • compliance_statusstring | null
  • last_calibration_atstring | null
  • next_due_atstring | null
  • retired_atstring | null

    Tombstone marker: the ISO-8601 instant this instrument was retired (decommissioned), or null while active. Populated on retired rows surfaced via ?include=retired.

  • deletedboolean

    Tombstone marker: true when this instrument has been retired and should be treated as removed by a mirror. Always false on the default active-only list.

    • Default: false
  • due_extensionDueExtension | null

    The active due-date extension (until, reason, extended_at, extended_by_user_id), or null when there is none or a newer calibration has superseded it. next_due_at already reflects it.

InstrumentUpdate #

Partial update body for PATCH /instruments/{instrument_id}. Only the keys you send are changed; unknown keys are rejected (422).

Fields

  • namestring | null
    • At least 1 characters
    • At most 255 characters
  • manufacturerstring | null
    • At most 255 characters
  • modelstring | null
    • At most 255 characters
  • serial_numberstring | null
    • At most 255 characters
  • categorystring | null
    • At most 255 characters
  • departmentstring | null
    • At most 255 characters
  • locationstring | null
    • At most 255 characters
  • tolerance_specstring | null
    • At most 255 characters
  • notesstring | null
    • At most 10000 characters
  • unit_of_measurestring | null
    • At most 64 characters
  • is_reference_standardboolean | null
  • calibration_interval_valueinteger | null
    • Minimum: 1
    • Maximum: 100000
  • calibration_interval_unitstring | null
    • Allowed values: days, months, years
  • calibration_interval_change_reasonstring | null

    Why the calibration interval is changing. Applies when the instrument already has an interval and the value or unit you send differs from it. The change is always written to the workspace activity log with this reason. Whether the reason is required depends on the deployment's CHANGE_REASON_RULES_ENFORCED setting: when it is on, the request fails with 422 and code interval_change_reason_required without one; when it is off, the change is accepted and logged with no reason. Send a reason now and your integration will not break when it turns on. Not needed when the interval is unchanged.

    • At most 1000 characters
  • customFieldsmap<string, string> | null

    Workspace-defined custom fields to set, as a flat map of label -> value. MERGES into the instrument's existing custom fields: labels you do not send are left alone, and sending a blank value removes that label. Omit the key entirely, or send null, to change nothing.

Fields not listed here are rejected.

LocationSummary #

A location in your workspace. Use it to resolve the location_id that instrument responses return and that the location_id filter accepts. Each location belongs to one site, given by site_id.

Fields

  • idstring (uuid)Required
  • namestring | null
  • site_idstring (uuid) | null

Pagination #

Offset/limit pagination metadata returned alongside every list.

Fields

  • limitintegerRequired
  • offsetintegerRequired
  • totalintegerRequired
  • has_morebooleanRequired

SecretRotatedResponse #

The rotate-secret response: the new raw secret shown once.

Fields

  • idstring (uuid)Required
  • secretstringRequired

    The new signing secret, shown ONLY here.

  • secret_prefixstring | null

SiteSummary #

Fields

  • idstring (uuid)Required
  • namestring | null
  • is_defaultboolean
    • Default: false
  • timezonestring | null

StandardSummary #

A compliance standard the workspace has selected.

Fields

  • keystringRequired
  • labelstringRequired

ValidationError #

Fields

  • locarray[string | integer]Required
  • msgstringRequired
  • typestringRequired
  • inputany
  • ctxobject

WebhookCreate #

Subscribe request. events omitted/empty means "all catalog events".

Fields

  • urlstringRequired

    HTTPS delivery URL. Validated for SSRF (must resolve to a public address; private/loopback/link-local/metadata targets are rejected).

    • At most 2048 characters
  • eventsarray[string] | null

    Subset of the v1 event catalog to receive. Omit or send an empty list to subscribe to ALL events. Unknown types are rejected (422).

  • descriptionstring | null

    Optional human-readable label for this endpoint.

    • At most 500 characters

Fields not listed here are rejected.

WebhookCreatedResponse #

The subscribe response. Carries the raw secret EXACTLY ONCE.

Fields

  • idstring (uuid)Required
  • urlstringRequired
  • eventsarray[string] | null

    Subscribed event types, or null for all catalog events.

  • descriptionstring | null
  • is_activeboolean
    • Default: true
  • secret_prefixstring | null

    Non-secret leading fragment of the signing secret (display only).

  • disabled_atstring | null
  • created_atstring | null
  • updated_atstring | null
  • secretstringRequired

    The HMAC signing secret, shown ONLY here. Store it now. It cannot be retrieved again. Use it to verify the Axiospec-Signature header.

WebhookDeliveryRead #

One row of the per-endpoint delivery log (observability/debugging).

Never carries the signing secret or request headers; response_snippet is a truncated, secret-free excerpt of the consumer's response body.

Fields

  • idstring (uuid)Required
  • event_idstring (uuid)Required
  • event_typestringRequired
  • statusstringRequired

    pending | failed | succeeded | exhausted.

  • attempt_countinteger
    • Default: 0
  • next_attempt_atstring | null
  • last_attempt_atstring | null
  • last_status_codeinteger | null
  • last_errorstring | null
  • response_snippetstring | null
  • created_atstring | null
  • updated_atstring | null

WebhookRead #

The safe endpoint projection. NEVER includes secret_key.

Fields

  • idstring (uuid)Required
  • urlstringRequired
  • eventsarray[string] | null

    Subscribed event types, or null for all catalog events.

  • descriptionstring | null
  • is_activeboolean
    • Default: true
  • secret_prefixstring | null

    Non-secret leading fragment of the signing secret (display only).

  • disabled_atstring | null
  • created_atstring | null
  • updated_atstring | null

WebhookUpdate #

PATCH request. Only supplied fields are applied (exclude-unset semantics).

Fields

  • urlstring | null

    New HTTPS delivery URL (re-validated for SSRF when changed).

    • At most 2048 characters
  • eventsarray[string] | null

    Replacement event subset (empty/null = all events).

  • descriptionstring | null

    Replacement label.

    • At most 500 characters
  • is_activeboolean | null

    Enable (true) or disable (false) delivery to this endpoint.

Fields not listed here are rejected.