API
Universal Tracker Values

Universal Tracker Values

GET /v1/universal-tracker-values

Model interface schema in TypeScript:

interface UniversalTrackerValue {
  // Unique Id - ObjectId (24 character hex string)
  _id: string,
 
  // Reference to the UniversalTracker, containing metadata information - ObjectId
  universalTrackerId: string,
 
  // Reporting level (node) Id - ObjectId
  initiativeId: string,
 
  status: 'created' | 'updated' | 'rejected' | 'verified',
 
  // Only allow to create "actual" type of surveys at the moment
  utrvType: 'actual',
 
  // Numeric or percentage type answers contains value here
  value?: number,
 
  // Complex object containg answer data based on the UniversalTracker valueType
  valueData?: {
    // Based on valueType it can have different shape of data stored here
    // Text: Max 5000 characters, Arrays: Max 100 items
    data?: string | string[] | Record<string, string> | Record<string, number>;
    // Table data: Max 1000 rows × 50 columns
    table?: { code: string, value: string | number}[][];
    // Not applicable type: 'na' = Not Applicable, 'nr' = Not Reported
    notApplicableType?: 'na' | 'nr';
    isImported?: boolean;
  },
 
  period: 'monthly' | 'quarterly' | 'yearly',
 
  // Period end date
  effectiveDate: Date,
 
  // Determine if evidence is required for underlying Universal Tracker Values
  evidenceRequired: boolean,
 
  // Determine if verification step is required for underlying Universal Tracker Values
  verificationRequired: boolean,
 
  // Survey Universal Tracker Values default private value
  isPrivate?: boolean,
 
  // Only populated when added to assurance process through Assurance Tracker app
  assuranceStatus?: 'created' | 'completed' | 'completed_open' | 'rejected' | 'restated' | 'partial',
 
  // Latest notes from stakeholders and verifiers
  notes?: {
    // Note from the data stakeholder/contributor
    stakeholder?: {
      date: Date;
      action: 'updated';
      note?: string;
    };
    // Note from the data verifier
    verifier?: {
      date: Date;
      action: 'verified' | 'rejected';
      note?: string;
    };
  },
 
 
  // Metadata information about overall question
  universalTracker?: {
    _id: string,
    type: 'kpi',
    valueType: UtrValueType,
    unitType?: SupportedMeasureUnits,
    unit?: string,
    instructions?: string;  // Additional instructions for answering the question
    valueValidation?: {
      min?: number;        // For numeric values
      max?: number;        // For numeric values
      valueList?: {
        type: 'list',
        // ValueList Id to look up available options - ObjectId
        listId: string
      };
      table?: {
          validation?: {
            maxRows: number;  // Default: 1000 rows max
          };
          columns: {
            type: string;
            code: string;
            name: string;     // Max: 500 characters
            shortName?: string;  // Max: 100 characters
            unit?: string;
            unitType?: string;
            numberScale?: string;
          }[];
      };
      decimal?: number;
    },
    numberScale?: string,
    name: string,
    valueLabel: string,
    typeCode?: string
  }
}
 
export enum SupportedMeasureUnits {
  time = 'time',
  area = 'area',
  mass = 'mass',
  volume = 'volume',
  energy = 'energy',
  currency = 'currency',
  co2Emissions = 'co2Emissions',
  partsPer = 'partsPer',
  numberScale = 'numberScale',
  length = 'length',
}
 
 
enum UtrValueType {
  // Stored in value property
  Number = 'number', // number
  Percentage = 'percentage', // number
 
  // Complex types stored in valueData.data (string | string[] | Record)
  Text = 'text', // string
  Date = 'date', // string
  ValueListMulti = 'valueListMulti', // string[]
  ValueList = 'valueList', // string
  NumericValueList = 'numericValueList', // Record<string, number>
  TextValueList = 'textValueList', // Record<string, string>
 
  // Stored in valueData.table property
  Table = 'table',
}

Get UniversalTrackerValue

Retrieve a single universal tracker value by id.

GET universal-tracker-values/:utrvId

Request

curl --location 'https://api.sg.g17.eco/v1/universal-tracker-values/62d9530de3e05eca98c84b49' \
--header 'Authorization: Bearer TOKEN_HERE'

Response

{
    "success": true,
    "data": {
        "_id": "62d9530de3e05eca98c84b49",
        "period": "yearly",
        "evidenceRequired": false,
        "verificationRequired": false,
        "status": "verified",
        "effectiveDate": "2022-07-21T13:22:20.575Z",
        "universalTrackerId": "607818272c2c251ef832bec9",
        "initiativeId": "62d9530ce3e05eca98c8465d",
        "valueData": {},
        "value": 500,
        "valueType": "number",
        "universalTracker": {
            "_id": "607818272c2c251ef832bec9",
            "name": "Volunteering Hours - Total",
            "valueLabel": "Total number of volunteering hours?",
            "instructions": "Please include all volunteering activities by employees during business hours.",
            "type": "kpi",
            "typeCode": "",
            "valueType": "number",
            "unit": "h",
            "unitType": "time"
        }
    }
}

Get UniversalTrackerValue History

Retrieve the complete history of changes for a universal tracker value.

GET /v1/universal-tracker-values/:utrvId/history

The history endpoint returns all historical changes made to a UTRV, sorted by date (most recent first). Each history entry includes the action type, timestamp, value at that point, and any associated notes or evidence.

Response structure:

interface HistoryResponse {
  universalTracker: {
    _id: string;
    name: string;
    valueLabel: string;
    type: string;
    valueType: UtrValueType;
    unit?: string;
    unitType?: string;
    numberScale?: string;
    instructions?: string;
  };
 
  history: {
    _id: string;
    action: 'created' | 'updated' | 'verified' | 'rejected';
    date: string; // ISO 8601 timestamp
    userId: string; // Resolve user details from users array
 
    // Value fields (for number/percentage types)
    value?: number;
    unit?: string;
    numberScale?: string;
 
    // Complex value data (for text/table/list types)
    valueData?: {
      data?: string | string[] | Record<string, string | number>;
      table?: { code: string; value: string | number }[][];
      notApplicableType?: 'na' | 'nr';
    };
 
    valueType?: UtrValueType;
    note?: string;
    assuranceStatus?: 'created' | 'completed' | 'completed_open' | 'rejected' | 'restated' | 'partial';
 
    // Evidence references - resolve full details from documents array
    evidenceData?: {
      documentId: string;
      description?: string;
    }[];
  }[];
 
  // Lookup arrays to resolve IDs from history entries
  users: {
    _id: string;
    firstName?: string;
    surname?: string;
  }[];
 
  documents: {
    _id: string;
    type: 'link' | 'file';
    url: string; // For links: the actual URL, for files: signed download URL (~1 hour expiry)
    name?: string;
    description?: string;
  }[];
}

Response Structure: The response is optimized to avoid data repetition by separating users and documents into lookup arrays:

  • history[].userId → Resolve user details from users[] array by matching _id
  • history[].evidenceData[].documentId → Resolve full document details from documents[] array by matching _id

Important Notes:

  • The evidenceData field includes a document ID and optional description.
  • Document URLs have different meanings:
    • Link documents: User-provided URL (not validated)
    • File documents: Signed download URL with temporary access (~1 hour expiry)

Use Cases:

  • Audit trail for data changes
  • Tracking data revisions over time
  • Understanding data verification workflow
  • Compliance and assurance reporting
  • Resolving who made changes (via userId → users lookup)
  • Accessing evidence documents (via evidence IDs → documents lookup)

Request

curl --location 'https://api.sg.g17.eco/v1/universal-tracker-values/62d9530de3e05eca98c84b49/history' \
--header 'Authorization: Bearer TOKEN_HERE'

Response

{
    "success": true,
    "data": {
        "universalTracker": {
            "_id": "607818272c2c251ef832bec9",
            "name": "Volunteering Hours - Total",
            "valueLabel": "Total number of volunteering hours?",
            "type": "kpi",
            "valueType": "number",
            "unit": "h",
            "unitType": "time"
        },
        "history": [
            {
                "_id": "674d123456789abc12345678",
                "action": "verified",
                "date": "2024-11-03T10:30:00.000Z",
                "userId": "507f1f77bcf86cd799439011",
                "value": 500,
                "unit": "h",
                "numberScale": "single",
                "valueType": "number",
                "note": "Verified by manager",
                "assuranceStatus": "completed"
            },
            {
                "_id": "674d123456789abc12345677",
                "action": "updated",
                "date": "2024-11-02T14:20:00.000Z",
                "userId": "507f1f77bcf86cd799439011",
                "value": 500,
                "unit": "h",
                "numberScale": "single",
                "valueType": "number",
                "note": "Updated with Q4 data",
                "evidenceData": [
                    {
                        "documentId": "62abc123def456789012345",
                        "description": "Q4 volunteering report"
                    }
                ]
            },
            {
                "_id": "674d123456789abc12345676",
                "action": "created",
                "date": "2024-11-01T09:00:00.000Z",
                "userId": "507f1f77bcf86cd799439011",
                "valueType": "number"
            }
        ],
        "users": [
            {
                "_id": "507f1f77bcf86cd799439011",
                "firstName": "John",
                "surname": "Smith"
            }
        ],
        "documents": [
            {
                "_id": "62abc123def456789012345",
                "type": "file",
                "name": "Q4_volunteering_report.pdf",
                "description": "Q4 volunteering report"
            }
        ]
    }
}

Update UniversalTrackerValue

Update a single universal tracker value by id.

⚠️

Input data is expected to be provided in the default universal tracker unit. Unit conversion is not supported at the moment.

PATCH universal-tracker-values/:utrvId

Single value update

Request

curl --location --request PATCH 'https://api.sg.g17.eco/v1/universal-tracker-values/6364c32a70a4d65de2b83906/update' \
--header 'Authorization: Bearer TOKEN_HERE' \
--form 'value="67"'
💡

Endpoint must use 'Content-Type': 'application/x-www-form-urlencoded' for file uploads

Response

{
    "success": true,
    "data": {
        "_id": "6364c32a70a4d65de2b83906",
        "value": 67,
        "valueData": {},
        "status": "verified",
        "valueType": "number",
        "effectiveDate": "2023-02-28T23:59:59.999Z",
        "initiativeId": "62d9218bb3bfaa00089ea678",
        "universalTrackerId": "603376a6a9530404ce748936",
        "verificationRequired": false,
        "evidenceRequired": false
    }
}
Single value update with evidence links

It's also possible to add evidence links.

Request

curl --location --request PATCH 'https://api.sg.g17.eco/v1/universal-tracker-values/62d9530de3e05eca98c84b49/update' \
--header 'Authorization: Bearer TOKEN_HERE' \
--form 'value="67"' \
--form 'evidenceLinks[0][type]="link"' \
--form 'evidenceLinks[0][link]="www.google.com"' \
--form 'evidenceLinks[0][public]="false"'

Response

{
    "success": true,
    "data": {
        "_id": "62d9530de3e05eca98c84b49",
        "value": 67,
        "valueData": {},
        "status": "verified",
        "valueType": "number",
        "effectiveDate": "2022-07-21T13:22:20.575Z",
        "initiativeId": "62d9530ce3e05eca98c8465d",
        "universalTrackerId": "607818272c2c251ef832bec9",
        "verificationRequired": false,
        "evidenceRequired": false
    }
}