Sustainability API Guide#
The Sustainability API enables you to programmatically manage sustainability programs, metrics, dimensions, and data points within the Workiva platform. Use it to sync data from external systems like data lakes, automate data collection across reporting periods, manage metric value statuses through review workflows, and integrate sustainability data with tasking for approval processes.
Important: The Sustainability API is only available as a paid add-on to the Workiva platform. If you’re not sure whether your workspace has this add-on enabled, reach out to your Customer Success Manager (CSM) to confirm before you start building.
Table of Contents#
Prerequisites & Authentication#
Before getting started, make sure you have credentials set up.
All requests to the Workiva API require:
A valid OAuth 2.0 Bearer token
The
X-Version: 2026-01-01header on every requestThe appropriate OAuth scopes for each operation (noted per endpoint below)
export TOKEN="your_access_token_here"
export PROGRAM_ID="your-program-uuid"
Important: Always include
X-Version: 2026-01-01on all requests. Mixing functionality between different API versions is not supported and may produce unpredictable results.
Pagination#
All list endpoints in the Sustainability API return paginated results. When a response includes an @nextLink field at the top level, there are more results available. You must follow @nextLink to retrieve the full dataset.
If you ignore @nextLink, your results will be silently truncated. For integrations that load hundreds of metrics or thousands of metric values, this can cause data loss without any error.
How to Follow Pagination#
@nextLink is an opaque URL returned by the server. Treat it as a black box; do not construct or modify it. Make a GET request to the exact URL provided, using the same auth headers, until the response no longer includes @nextLink.
You can also control pagination behavior with these query parameters on the initial request:
$maxpagesizesets the maximum number of results per page (e.g.,$maxpagesize=50).$nextis an explicit pagination cursor. In most cases you should follow@nextLinkinstead of constructing$nextvalues yourself.
NEXT_LINK="https://api.app.wdesk.com/programs?..."
while [ -n "$NEXT_LINK" ]; do
RESPONSE=$(curl -s -G "${NEXT_LINK}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01")
echo "$RESPONSE" | jq '.data[]'
NEXT_LINK=$(echo "$RESPONSE" | jq -r '.["@nextLink"] // empty')
done
Note:
@nextLinkapplies to all list endpoints:GET /programs,GET /programs/{programId}/metrics,GET /programs/{programId}/metrics/{metricId}/values, and others. Always paginate to completion before processing results.
Key Concepts#
Concept |
Description |
|---|---|
Program |
A sustainability program is the top-level container that organizes all sustainability data within a workspace. It holds topics, metrics, and dimensions. Program names must be unique within a workspace. |
Topic |
An organizational grouping mechanism for metrics. Topics can be nested (a topic can have a |
Metric |
A trackable data point within a program, such as “Scope 1 GHG Emissions” or “Total Energy Consumption”. Metrics have a |
Dimension |
A categorization axis that allows collecting multiple values per metric. Common dimensions include facility location, business unit, market region, or demographic breakdowns. A metric can use up to 7 dimensions, and each dimension can have up to 1,500 values. |
Metric Value |
A single data record belonging to a metric for a specific reporting period and dimension coordinate. For example, the Scope 1 emissions value for “Ames, IA” in Q1 2025. |
Reporting Period |
Defines the time window for a metric value using |
Value Status |
The workflow state of a metric value: |
Coordinates |
A mapping of dimension IDs to dimension value IDs that identifies which “slice” of a metric a value belongs to. For example, |
Framework Reference |
An association linking a metric to a reporting framework or standard (e.g., GRI 2-6-b-iii, SASB, TCFD), including the framework name, published version, and reference identifier. |
Tag |
A key-value pair associated with a metric for classification, for example |
How Sustainability Programs Are Structured#
A sustainability program follows a hierarchical structure:
Program
├── Dimensions (e.g., Facility, Region)
│ └── Dimension Values (e.g., "Ames, IA", "Portland, OR")
├── Topics (e.g., Climate, Water, Waste)
│ └── Subtopics (nested via parent reference)
└── Metrics (e.g., Scope 1 GHG Emissions)
├── Framework References (GRI, SASB, TCFD)
├── Tags (Industry: Agriculture)
└── Metric Values (data points per reporting period + coordinates)
├── Reporting Period (year, start month, end month)
├── Coordinates (dimension → dimension value mappings)
├── Status (notStarted → inProgress → complete)
└── Task (optional linked task for review/approval)
Metrics are organized under topics, and each metric value is uniquely identified by the combination of its reporting period and coordinates. This means you can have one value per metric per unique (reporting period + coordinate) combination.
Note: A metric value’s identity is determined by its
reportingPeriodandcoordinates. If you create two values with the same reporting period and coordinates for the same metric, the batch upsert endpoint will update the existing value rather than creating a duplicate.
Managing Programs#
Listing Programs#
Retrieve all sustainability programs in the current workspace.
Required scope: file:read
GET /programs
curl -X GET "https://api.app.wdesk.com/programs" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"@nextLink": "<opaque_url>",
"data": [
{
"id": "ca72b647-8e43-44c3-a4e7-2aa3294c37ad",
"name": "Sustainability Program"
}
]
}
You can filter programs by name using the $filter query parameter.
curl -G "https://api.app.wdesk.com/programs" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=name eq "ESG Reporting 2025"'
Tip: Use
$filter=name eq "..."to find a specific program by name when you don’t have its ID. This is particularly useful when mapping programs from an external system.
Retrieving a Single Program#
Required scope: file:read
GET /programs/{programId}
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"id": "ca72b647-8e43-44c3-a4e7-2aa3294c37ad",
"name": "Sustainability Program"
}
Creating a Program#
Required scope: file:write
POST /programs
Request Body:
{
"name": "ESG Reporting 2025"
}
curl -X POST "https://api.app.wdesk.com/programs" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"id": "ca72b647-8e43-44c3-a4e7-2aa3294c37ad",
"name": "ESG Reporting 2025"
}
Note: Program names must be unique within a workspace. Attempting to create a program with a duplicate name will return a
409 Conflicterror.
Renaming a Program#
Required scope: file:write
PATCH /programs/{programId}
Request Body:
[
{
"op": "replace",
"path": "/name",
"value": "ESG Reporting 2025 - Draft"
}
]
curl -X PATCH "https://api.app.wdesk.com/programs/${PROGRAM_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (200 OK):
{
"id": "ca72b647-8e43-44c3-a4e7-2aa3294c37ad",
"name": "ESG Reporting 2025 - Draft"
}
Warning: Only the
/nameproperty can be updated via PATCH. Only one property may be updated at a time.
Managing Topics#
Topics provide organizational structure for your metrics. They can be nested to create a hierarchical outline.
Creating a Topic#
Required scope: file:write
POST /programs/{programId}/topics
Request Body:
{
"name": "Climate",
"index": 0
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/topics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"id": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"name": "Climate",
"index": 0,
"parent": null
}
Creating a Subtopic#
To create a subtopic, include the parent field referencing the parent topic’s ID.
Request Body:
{
"name": "Emissions",
"index": 0,
"parent": "cc507098-c403-4b8b-98b4-31a6c5a639f4"
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/topics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"id": "dd618209-d504-5c9c-a5f8-42b4305d98e5",
"name": "Emissions",
"index": 0,
"parent": "cc507098-c403-4b8b-98b4-31a6c5a639f4"
}
Tip: Set the
indexproperty to control the ordering of topics in the program outline. Ifindexis not set, the topic will be ordered last.
Updating a Topic#
Required scope: file:write
PATCH /programs/{programId}/topics/{topicId}
Only one property may be updated at a time. The following properties support PATCH:
Path |
Supported Operations |
|---|---|
|
|
|
|
|
|
Request Body:
[
{
"op": "replace",
"path": "/name",
"value": "Climate & Energy"
}
]
curl -X PATCH "https://api.app.wdesk.com/programs/${PROGRAM_ID}/topics/${TOPIC_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (200 OK):
{
"id": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"name": "Climate & Energy",
"index": 0,
"parent": null
}
Deleting a Topic#
Required scope: file:write
DELETE /programs/{programId}/topics/{topicId}
curl -X DELETE "https://api.app.wdesk.com/programs/${PROGRAM_ID}/topics/${TOPIC_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Version: 2026-01-01"
Response (204 No Content)
Warning: Deleting a topic does not delete the metrics beneath it. Metrics that belonged to the deleted topic will no longer have a topic assignment. Reassign them with a PATCH before deleting the topic if needed.
Managing Dimensions#
Dimensions allow you to collect multiple values per metric across different categories, such as facility locations, business units, or demographic groups.
Listing Dimensions#
Required scope: file:read
GET /programs/{programId}/dimensions
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"data": [
{
"active": true,
"id": "c38353ce-32bc-4ea7-852a-bf5e12b72d76",
"name": "Facility"
}
]
}
Note: When listing dimensions, the
valuesarray isnull. To retrieve the full list of dimension values, fetch the individual dimension by ID.
Retrieving a Dimension with Its Values#
Required scope: file:read
GET /programs/{programId}/dimensions/{dimensionId}
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions/${DIMENSION_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"active": true,
"id": "c38353ce-32bc-4ea7-852a-bf5e12b72d76",
"name": "Facility",
"values": [
{
"id": "Ames, IA",
"name": "Ames, Iowa",
"active": true
},
{
"id": "Portland, OR",
"name": "Portland, Oregon",
"active": true
}
]
}
Key fields:
id: The system-generated UUID for the dimension.values: The list of dimension values. Each value has a user-enteredid(up to 20 characters), an optionalname(up to 300 characters, defaults toidif not provided), and anactiveboolean indicating whether the value is available for use in new metric values.active: Whether the dimension is available for use in new metrics.
Important: Dimension value
idfields are user-entered identifiers (up to 20 characters), not system-generated UUIDs. These IDs are what you use in thecoordinatesmapping when creating metric values.
Creating a Dimension#
Required scope: file:write
POST /programs/{programId}/dimensions
Request Body:
{
"active": true,
"name": "Facility",
"values": [
{
"id": "Ames, IA",
"name": "Ames, Iowa",
"active": true
},
{
"id": "Portland, OR",
"name": "Portland, Oregon",
"active": true
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"active": true,
"id": "c38353ce-32bc-4ea7-852a-bf5e12b72d76",
"name": "Facility",
"values": [
{
"id": "Ames, IA",
"name": "Ames, Iowa",
"active": true
},
{
"id": "Portland, OR",
"name": "Portland, Oregon",
"active": true
}
]
}
Note: A dimension must have at least 1 value and can have up to 1,500 values. Plan your dimension values carefully, as the
idfield on each value is what you will use in metric valuecoordinates.
Updating a Dimension#
Required scope: file:write
PATCH /programs/{programId}/dimensions/{dimensionId}
Only one property may be updated at a time. The following properties support PATCH:
Path |
Supported Operations |
|---|---|
|
|
|
|
|
|
Request Body:
[
{
"op": "replace",
"path": "/name",
"value": "Office Location"
}
]
curl -X PATCH "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions/${DIMENSION_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (200 OK):
{
"active": true,
"id": "c38353ce-32bc-4ea7-852a-bf5e12b72d76",
"name": "Office Location",
"values": null
}
Tip: Use PATCH with
/valuesto add or update dimension values after the dimension has been created. Use/activeto deactivate a dimension without deleting it.
Managing Metrics#
Metrics are the trackable data points in your sustainability program. Each metric has a data type, unit, and belongs to a topic.
Listing Metrics#
Required scope: file:read
GET /programs/{programId}/metrics
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"data": [
{
"code": 12,
"dataType": "number",
"description": "Covers all direct greenhouse gas emissions from sources owned or controlled by the reporting entity.",
"id": "ae82b647-8e43-44c3-a4e7-2aa3294c87ac",
"index": 0,
"name": "Scope 1 Consolidated GHG Emissions",
"requireNotes": false,
"requireSupportingAttachments": false,
"strCode": "M12",
"topic": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"unit": "Metric Ton"
}
]
}
Filtering Metrics#
You can filter metrics by many properties including name, dataType, topic, code, and unit.
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=name contains "GHG"'
Filter Field |
Supported Predicates |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Tip: Use
$filter=name contains "..."to search for metrics by keyword when mapping from an external system where you know the metric name but not the Workiva ID.
Creating a Metric#
Required scope: file:write
POST /programs/{programId}/metrics
Request Body:
{
"name": "Scope 1 Consolidated GHG Emissions",
"dataType": "number",
"unit": "Metric Ton",
"description": "Covers all direct greenhouse gas emissions from sources owned or controlled by the reporting entity.",
"topic": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"allowedDimensions": ["c38353ce-32bc-4ea7-852a-bf5e12b72d76"],
"requireNotes": false,
"requireSupportingAttachments": false,
"frameworkReferences": [
{
"framework": "GRI",
"publishedVersion": "2016.2",
"referenceName": "305-1"
}
],
"tags": [
{
"name": "Category",
"value": "Environment"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"code": 14,
"dataType": "number",
"description": "Covers all direct greenhouse gas emissions from sources owned or controlled by the reporting entity.",
"id": "ae82b647-8e43-44c3-a4e7-2aa3294c87ac",
"index": 0,
"name": "Scope 1 Consolidated GHG Emissions",
"requireNotes": false,
"requireSupportingAttachments": false,
"strCode": "M14",
"topic": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"unit": "Metric Ton"
}
Key fields:
dataType: One oftext,number,date,currency,percent.allowedDimensions: Up to 7 dimension IDs that can be used when creating values for this metric.topic: The UUID of the topic this metric belongs to.code: An optional integer identifier. If not provided, the system assigns the next available integer.strCode: The system-generated string representation (e.g.,M14), read-only.frameworkReferences: Up to 20 framework associations (e.g., GRI, SASB, TCFD).tags: Up to 20 key-value tag pairs for classification.
Updating a Metric#
Required scope: file:write
PATCH /programs/{programId}/metrics/{metricId}
Only one property may be updated at a time. The following properties support PATCH:
Path |
Supported Operations |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Request Body:
[
{
"op": "replace",
"path": "/unit",
"value": "Metric Tons CO2e"
}
]
curl -X PATCH "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (200 OK):
{
"code": 14,
"dataType": "number",
"description": "Covers all direct greenhouse gas emissions from sources owned or controlled by the reporting entity.",
"id": "ae82b647-8e43-44c3-a4e7-2aa3294c87ac",
"index": 0,
"name": "Scope 1 Consolidated GHG Emissions",
"requireNotes": false,
"requireSupportingAttachments": false,
"strCode": "M14",
"topic": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"unit": "Metric Tons CO2e"
}
Deleting a Metric#
Required scope: file:write
DELETE /programs/{programId}/metrics/{metricId}
curl -X DELETE "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Version: 2026-01-01"
Response (204 No Content)
Warning: Deleting a metric permanently removes it and all associated metric values. This action is irreversible.
Loading and Updating Metric Values#
Metric values are the actual data points collected for each metric. Each value is identified by its reporting period and coordinates (dimension mappings).
Creating a Single Metric Value#
Required scope: file:write
POST /programs/{programId}/metrics/{metricId}/values
Request Body:
{
"value": "512.8768743",
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 12
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"status": "notStarted",
"notes": "Sourced from ERP system export on 2025-01-15"
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (201 Created):
{
"value": "512.8768743",
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 12
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
}
}
Key fields:
value: Always a string, even for numeric metrics. Up to 32,767 characters.reportingPeriod: Defines the time window. Use month integers: Q1 = start: 1, end: 3; Q2 = start: 4, end: 6; full year = start: 1, end: 12.coordinates: Maps dimension IDs (UUIDs) to dimension value IDs (the user-entered short IDs). Up to 3 coordinate pairs. Omit if the metric has no dimensions.status: The initial workflow status. See Managing Value Status for valid transitions.notes: Optional notes (up to 32,767 characters).task: Optional task ID to link this value to a task for review/approval workflows.
Important: The
coordinatesmapping uses the dimension’s UUID as the key and the dimension value’s user-enteredidas the value, not the dimension value’s name.
Listing Metric Values#
Required scope: file:read
GET /programs/{programId}/metrics/{metricId}/values
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"data": [
{
"id": "bf9aa2c3-f278-4c77-8acb-97584e843dcd",
"value": "512.8768743",
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 12
},
"status": "notStarted",
"notes": "Sourced from ERP system export on 2025-01-15",
"task": "VGFMDU1MmJiOGEwZDVjZWYzNDlVhZDWU5Y2jYzY3zax5iMg"
}
]
}
Deleting a Single Metric Value#
Required scope: file:write
DELETE /programs/{programId}/metrics/{metricId}/values/{valueId}
curl -X DELETE "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/${VALUE_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Version: 2026-01-01"
Response (204 No Content)
Use this endpoint when you need to delete one specific value and you have its id. For deleting multiple values at once, use the Batch Deleting Metric Values endpoint instead.
Updating a Single Metric Value#
Required scope: file:write
PATCH /programs/{programId}/metrics/{metricId}/values/{metricValueId}
Only one property may be updated at a time. The following properties support PATCH:
Path |
Supported Operations |
|---|---|
|
|
|
|
Request Body:
[
{
"op": "replace",
"path": "/notes",
"value": "Updated notes after review"
}
]
curl -X PATCH "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/${VALUE_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (200 OK):
{
"coordinates": {},
"id": "bf9aa2c3-f278-4c77-8acb-97584e843dcd",
"notes": "Updated notes after review",
"reportingPeriod": {
"end": 12,
"start": 1,
"year": 2025
},
"value": "512.8768743"
}
Tip: Use PATCH when you need to update a single value’s
notesorvaluefield without going through the batch upsert flow.
Filtering Metric Values by Reporting Period#
Use the $filter query parameter to retrieve values for specific reporting periods.
Full Year Values#
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=reportingPeriod.year eq 2025'
Quarterly Values#
Retrieve only Q1 2025 values:
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=reportingPeriod.year eq 2025 and reportingPeriod.start eq 1 and reportingPeriod.end eq 3'
Available Filter Fields#
Field |
Supported Predicates |
|---|---|
|
|
|
|
|
|
|
|
|
|
Tip: Use
reportingPeriod.year ge 2024 and reportingPeriod.year le 2025to retrieve values across multiple years for year-over-year comparison.
Managing Value Status#
Metric values have a workflow status that tracks their progress through the data collection and review process.
Status Values#
Status |
Description |
|---|---|
|
No data has been entered yet. This is the starting state. |
|
Data has been entered but the task has not been sent to assignees. |
|
The assignee is actively working on the data point. |
|
Data has been submitted and is awaiting approval. |
|
The approver has returned the data for revision. |
|
Data has been finalized and approved. |
|
An error occurred during processing. |
Status Transition Rules#
The status field on a metric value can only be changed directly via the API for two transitions. All other transitions are driven by the task approval workflow and cannot be set via the API directly.
Transition |
Trigger |
How |
|---|---|---|
|
Reset a value for re-entry |
Set |
|
Mark as final without a task |
Set |
|
Task created but not sent |
Automatic (task workflow) |
|
Task sent to assignee |
Automatic (task workflow) |
|
Assignee submits ( |
Task action submission |
|
Approver approves ( |
Task action submission |
|
Approver rejects ( |
Task action submission |
|
Assignee revises and resubmits |
Automatic (task workflow) |
Any status → |
Processing failure |
Automatic (system-set; cannot be set via API) |
Important: Attempting to set
statustocompletefrom any state other thannotStartedwill return a validation error. If a value is ininProgress,sentForApproval, orreturned, its status must advance through the task approval workflow.
To mark a value as complete (from notStarted):
Request Body:
{
"data": [
{
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 12
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"status": "complete",
"value": "512.8768743"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/batchUpsertion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Tip: When loading data from a data lake where the values are already considered final, set
statustocompleteduring the upsert. If the data still requires human review, leave the status asnotStartedor omit it, and use the task approval workflow to manage the review process.
Batch Upserting Metric Values#
The batch upsert endpoint is the most efficient way to load or update multiple metric values at once. It handles both creating new values and updating existing ones in a single request.
Required scope: file:write
POST /programs/{programId}/metrics/{metricId}/values/batchUpsertion
Request Body:
{
"data": [
{
"value": "512.87",
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 3
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"status": "notStarted",
"notes": "Q1 2025 - imported from data lake"
},
{
"value": "489.23",
"reportingPeriod": {
"year": 2025,
"start": 4,
"end": 6
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"status": "notStarted",
"notes": "Q2 2025 - imported from data lake"
},
{
"value": "601.44",
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 3
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Portland, OR"
},
"status": "notStarted"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/batchUpsertion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (202 Accepted):
{
"operationLocation": "https://api.app.wdesk.com/operations/128f274395254cf17eda6b3eb3d021b9"
}
Response Headers:
Location: https://api.app.wdesk.com/operations/128f274395254cf17eda6b3eb3d021b9
Retry-After: 5
This is an asynchronous operation. Poll the operationLocation URL to check the status.
Polling for Completion#
GET /operations/{operationId}
curl -X GET "https://api.app.wdesk.com/operations/${OPERATION_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Version: 2026-01-01"
Response (in progress):
{
"id": "128f274395254cf17eda6b3eb3d021b9",
"status": "started",
"created": { "dateTime": "2026-01-15T10:00:00Z" },
"updated": { "dateTime": "2026-01-15T10:00:05Z" }
}
Response (completed):
{
"id": "128f274395254cf17eda6b3eb3d021b9",
"status": "completed",
"resourceUrl": "https://api.app.wdesk.com/operations/128f274395254cf17eda6b3eb3d021b9/results",
"created": { "dateTime": "2026-01-15T10:00:00Z" },
"updated": { "dateTime": "2026-01-15T10:00:30Z" }
}
Important: Always respect the
Retry-Afterheader when polling. The operations endpoint is rate-limited. Polling too frequently may result in429 Too Many Requestserrors.
How Upsertion Identifies Existing Values#
For each value in the batch, provide either:
The value’s
id(if you know it from a previous GET), orBoth the
reportingPeriodandcoordinates
If both are provided, the id takes precedence. When matching by reporting period and coordinates, an existing value with the same combination will be updated; otherwise, a new value is created.
Warning: The payload is limited to 10 MB. Break larger payloads into multiple requests. If any value in the batch fails validation, no changes are stored and the entire batch is rolled back.
Updating Existing Values#
To update an existing data point’s value, use the batch upsert endpoint with the same reporting period and coordinates. The new value and any other provided fields will overwrite the existing record.
Request Body:
{
"data": [
{
"value": "525.10",
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 3
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
},
"notes": "Q1 2025 - corrected value after audit"
}
]
}
Clearing Fields on Update#
Use the fieldsToClear array to explicitly clear optional fields during an upsert. This is the only way to remove a value’s notes or other nullable fields.
Request Body:
{
"data": [
{
"id": "bf9aa2c3-f278-4c77-8acb-97584e843dcd",
"value": "525.10",
"fieldsToClear": ["notes"]
}
]
}
Batch Deleting Metric Values#
Required scope: file:write
POST /programs/{programId}/metrics/{metricId}/values/batchDeletion
For each value to delete, provide either the id or both the reportingPeriod and coordinates.
Request Body:
{
"data": [
{
"reportingPeriod": {
"year": 2025,
"start": 1,
"end": 3
},
"coordinates": {
"c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA"
}
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/batchDeletion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (204 No Content)
Warning: Batch deletion is immediate and irreversible. There is no undo. Verify your reporting period and coordinates carefully before executing.
Linking Metric Values to Workiva Documents#
Metric values can be connected to live data in a Workiva spreadsheet or document using the dataSource field. When a dataSource is set, the metric value reflects data from the linked cell or region, connecting sustainability data directly to your financial reporting documents.
Connection Types#
|
Required Fields |
Description |
|---|---|---|
|
|
Links to a specific cell in a Workiva spreadsheet |
|
|
Links to a cell within a table in a Workiva document |
|
|
Links to a text region within a Workiva document |
Example: Linking to a Spreadsheet Cell#
Include dataSource in your batch upsert request body alongside the value:
{
"data": [
{
"value": "512.87",
"reportingPeriod": { "year": 2025, "start": 1, "end": 3 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA" },
"dataSource": {
"connectionType": "spreadsheetCell",
"spreadsheetCellConnection": {
"spreadsheet": "7a5e271acf1d49d480a6fbabc394a0fa",
"sheet": "576696e0f7a143b4a0bc7c20a34480ab",
"cell": "A1"
}
}
}
]
}
Example: Linking to a Document Table Cell#
{
"dataSource": {
"connectionType": "tableCell",
"tableCellConnection": {
"document": "3f2a891bcf1d49d480a6fbabc394a012",
"section": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"table": "b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5",
"cell": "B3"
}
}
}
Tip: The
dataSourcefield is available on bothPOST(single value create) andbatchUpsertion. Use it when your sustainability program pulls data that also lives in a Workiva spreadsheet, so the two stay in sync without manual re-entry.
Supporting Attachments#
The supportingAttachments field on a metric value holds evidence or source documentation linked to that data point. It is read-only via the API (populated through the Workiva UI or task workflow) and is included in GET responses when attachments are present.
Schema#
Each attachment is a MetricAttachment object with these fields:
Field |
Type |
Description |
|---|---|---|
|
string (required) |
Display name of the attachment |
|
string (required) |
One of: |
A metric value can have up to 50 supporting attachments.
Example GET Response with Attachments#
{
"id": "bf9aa2c3-f278-4c77-8acb-97584e843dcd",
"value": "512.8768743",
"status": "sentForApproval",
"supportingAttachments": [
{
"name": "Q1 2025 ERP Export",
"type": "file"
},
{
"name": "https://internal.example.com/reports/q1-ghg",
"type": "url"
}
]
}
Note: You cannot add or remove
supportingAttachmentsvia the API. Attachments are managed in the Workiva UI or through the task workflow. When an agent uses attachments as part of an approval decision (see Use Case: Agent-Based Data Analysis and Auto-Review), check whethersupportingAttachmentsis non-empty rather than inspecting specific attachment content.
Working with Tasks for Sustainability#
Each metric value can be linked to a task for review and approval workflows. The task field on a metric value contains the task ID, which you can use with the Tasks API to manage the approval process.
Listing Tasks for a Sustainability Program#
Use the Tasks API with a location.resource filter to find tasks associated with your program.
Required scope: task:read
GET /tasks
curl -G "https://api.app.wdesk.com/tasks" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=location.resource eq "ca72b647-8e43-44c3-a4e7-2aa3294c37ad"'
Response (200 OK):
{
"data": [
{
"id": "129g274495354cf18edb6b3ea3d023b2",
"title": "Review Q1 2025 Scope 1 Emissions - Ames, IA",
"description": "Please review and validate the Q1 2025 Scope 1 GHG emissions value for Ames, IA.",
"status": "Created",
"assignees": [
{
"id": "V1ZVd2VyFzU3NiQ1NDA4NjIzNzk2MjD",
"type": "USER"
}
],
"dueAt": "2026-02-15T00:00:00Z",
"location": {
"resource": "ca72b647-8e43-44c3-a4e7-2aa3294c37ad",
"segment": "ae82b647-8e43-44c3-a4e7-2aa3294c87ac"
},
"approvalSteps": [
{
"completionMode": "ONE",
"dueAt": null,
"participants": [
{
"id": "V0ZVc2VyHzY0MTI5NzM0MzkxODg5OTI",
"type": "USER"
}
],
"responses": []
}
],
"modified": {
"dateTime": "2026-01-16T08:00:00Z"
},
"created": {
"dateTime": "2026-01-15T10:00:00Z",
"user": {
"displayName": "System Admin",
"email": "admin@example.com",
"id": "V3ZVc2VyFzV3NiQ5NDA2NjIzNxk2njH"
}
}
}
]
}
Note: The Tasks API returns both general tasks and sustainability-linked tasks. Use the
location.resourcefilter shown above to scope results to a specific program. ThelocationandapprovalStepsfields appear on tasks that have approval workflows configured.
Task Status and Approval Flow#
Sustainability tasks follow a multi-step approval process:
Assignee receives the task and enters/validates the metric value
Assignee submits the task for approval (
SUBMITaction)Approver(s) review and either approve (
APPROVE) or reject (REJECT)If rejected, the task is returned to the assignee for revision
Use the POST /tasks/{taskId}/actionSubmission endpoint to submit actions programmatically. Available actions: SUBMIT, APPROVE, REJECT, CANCEL, RESTART, SKIP.
Note: The Tasks API returns both general tasks and sustainability-associated tasks. It does not return tasks created as part of a process. Filter by
location.resourceto scope results to a specific program.
Managing Program Permissions#
Program permissions control which users and groups can access a sustainability program and in what capacity. Permissions are referenced by permission UUID, not by role name. Use GET /permissions to list the available permission definitions (e.g., Editor, Viewer, Owner) and their UUIDs for your workspace.
Retrieving Program Permissions#
Required scope: file:read
GET /programs/{programId}/permissions
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/permissions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
Response (200 OK):
{
"@nextLink": "<opaque_url>",
"data": [
{
"permission": "85aa87ee-beb9-4417-8fa0-420e9de63534",
"principal": "V0ZVc2VyHzU2NDg2NjU2MjQ0NDQ5Mjg",
"principalType": "user",
"resource": "014b90fd-0631-422c-b94e-1240c53f1d6d"
},
{
"permission": "85aa87ee-beb9-4417-8fa0-420e9de63534",
"principal": "V0ZVc2VyHzUQ0NDQ5Mjg2NDg2NjU2Mj",
"principalType": "group",
"resource": "014b90fd-b94e-0631-422c-1240c53f1d6d"
}
]
}
Key fields:
permission: The UUID of the permission definition (e.g., Editor, Viewer). Look up permission names viaGET /permissions.principal: The string ID of the user or group.principalType: Eitheruserorgroup.resource: The UUID of the program the permission applies to (read-only).
You can filter by permission or principal using the $filter query parameter.
Modifying Program Permissions#
Required scope: file:write
POST /programs/{programId}/permissions/modification
The modification endpoint accepts two arrays: toAssign for granting permissions and toRevoke for removing them. Each entry requires a permission UUID and a principal ID. To change a user’s permission level (e.g., from Viewer to Editor), revoke the old permission and assign the new one in the same request.
Request Body:
{
"toAssign": [
{
"permission": "598e8fa3-3e7c-4fb7-b662-f44522216e2b",
"principal": "V0ZVc2VyHzU2NDg2NjU2MjQ0NDQ5Mjg"
}
],
"toRevoke": [
{
"permission": "85aa87ee-beb9-4417-8fa0-420e9de63534",
"principal": "V0ZVc2VyHzU2NDg2NjU2MjQ0NDQ5Mjg"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/permissions/modification" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Response (204 No Content)
If any modification in the request fails, all modifications in that request fail.
Note: You must have owner-level access to a program to modify its permissions. Users without the appropriate role will receive a
403 Forbiddenresponse.
Use Case: Syncing Data from a Data Lake#
This section walks through a complete workflow for syncing sustainability data from an external system (such as a data warehouse, ERP, or data lake) into a Workiva sustainability program.
Overview#
The sync workflow involves:
Map your external IDs to Workiva program, metric, and dimension IDs
Check if metrics/dimensions exist, and create any that are missing
Load data points using the batch upsert endpoint
Set the appropriate value status based on your data quality workflow
Step 1: Discover or Create the Program#
First, find your target program by name:
curl -G "https://api.app.wdesk.com/programs" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=name eq "ESG Reporting 2025"'
If the program does not exist, create it:
curl -X POST "https://api.app.wdesk.com/programs" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d '{"name": "ESG Reporting 2025"}'
Store the returned id as your PROGRAM_ID.
Step 2: Sync Dimensions#
List existing dimensions and compare against your external system’s categories:
curl -X GET "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01"
For each dimension in your external system that does not exist in Workiva, create it:
Request Body:
{
"active": true,
"name": "Business Unit",
"values": [
{ "id": "BU-001", "name": "North America Operations", "active": true },
{ "id": "BU-002", "name": "European Operations", "active": true },
{ "id": "BU-003", "name": "Asia Pacific Operations", "active": true }
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/dimensions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Tip: Maintain a mapping table in your integration layer that maps external system IDs to Workiva dimension IDs and dimension value IDs. Dimension value
idfields are limited to 20 characters, so use short codes (e.g.,BU-001) rather than full names.
Step 3: Sync Metrics#
List existing metrics and check for any that need to be created:
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=name contains "Scope 1"'
If the metric does not exist, create it with the appropriate topic and allowed dimensions:
Request Body:
{
"name": "Scope 1 GHG Emissions",
"dataType": "number",
"unit": "Metric Tons CO2e",
"topic": "cc507098-c403-4b8b-98b4-31a6c5a639f4",
"allowedDimensions": ["c38353ce-32bc-4ea7-852a-bf5e12b72d76"]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Step 4: Load Data Points#
Use the batch upsert endpoint to load data for each metric. The upsert handles both creating new values and updating existing ones.
Request Body:
{
"data": [
{
"value": "1024.55",
"reportingPeriod": { "year": 2025, "start": 1, "end": 3 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA" },
"status": "notStarted",
"notes": "Data lake sync - 2026-01-15T08:00:00Z"
},
{
"value": "987.32",
"reportingPeriod": { "year": 2025, "start": 4, "end": 6 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA" },
"status": "notStarted",
"notes": "Data lake sync - 2026-01-15T08:00:00Z"
},
{
"value": "1155.80",
"reportingPeriod": { "year": 2025, "start": 1, "end": 3 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Portland, OR" },
"status": "notStarted",
"notes": "Data lake sync - 2026-01-15T08:00:00Z"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/batchUpsertion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Poll the returned operationLocation URL until the operation completes.
Step 5: Decide on Value Status#
Your choice of initial status depends on whether the data lake values are considered final:
Scenario |
Recommended Status |
Rationale |
|---|---|---|
Data lake values are authoritative and pre-validated |
|
No further review is needed. Mark as final immediately. |
Data requires human review before finalization |
|
Leave in starting state so reviewers can process through the task workflow. |
Data needs selective review based on thresholds |
|
Load as |
Tip: Wdata tables can serve as a staging area for mapping and validating external data before loading into the sustainability program. Use the Wdata Preparation API to import data into a table, perform transformations and mapping, then use the Sustainability API to load the validated data points.
Use Case: Agent-Based Data Analysis and Auto-Review#
This section describes how an automated agent can analyze loaded data and make intelligent decisions about which values need human review versus which can be auto-approved.
Threshold-Based Analysis#
After loading data from a data lake, an automated process can compare current period values against prior year data to determine whether human review is warranted.
Step 1: Retrieve Current and Prior Year Values#
Fetch values for both the current and prior reporting periods:
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=reportingPeriod.year eq 2025'
curl -G "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=reportingPeriod.year eq 2024'
Step 2: Apply Threshold Logic#
In your application logic, compare values across periods. Example thresholds:
Condition |
Action |
|---|---|
Delta is less than 10% and less than $10,000 |
Auto-complete the value (low risk) |
Delta exceeds 10% or exceeds $10,000 |
Flag for human review, create/update a task |
No prior year value exists |
Flag for human review (new data point) |
Step 3: Auto-Complete Low-Risk Values#
For values that fall within acceptable thresholds, batch upsert with status: "complete":
Request Body:
{
"data": [
{
"reportingPeriod": { "year": 2025, "start": 1, "end": 3 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Ames, IA" },
"value": "512.87",
"status": "complete",
"notes": "Auto-approved: delta from prior year is 2.1% ($10.52)"
}
]
}
curl -X POST "https://api.app.wdesk.com/programs/${PROGRAM_ID}/metrics/${METRIC_ID}/values/batchUpsertion" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Step 4: Flag High-Risk Values for Review#
For values that exceed the threshold, leave the status as notStarted and add a note explaining why review is needed:
Request Body:
{
"data": [
{
"reportingPeriod": { "year": 2025, "start": 1, "end": 3 },
"coordinates": { "c38353ce-32bc-4ea7-852a-bf5e12b72d76": "Portland, OR" },
"value": "1155.80",
"status": "notStarted",
"notes": "REVIEW REQUIRED: delta from prior year is 18.7% ($182.36). Prior year value: 973.44"
}
]
}
Agent as Final Reviewer#
An automated agent can also serve as the third and final reviewer in a multi-step approval process. After human reviewers have approved or entered data, the agent can:
Retrieve tasks in
sentForApprovalstatus for the programCheck each value for proper supporting documentation (notes, attachments)
Auto-approve values that have adequate support using the task action submission endpoint
Auto-reject values that lack required notes or supporting attachments
curl -G "https://api.app.wdesk.com/tasks" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Accept: application/json" \
-H "X-Version: 2026-01-01" \
--data-urlencode '$filter=location.resource eq "'"${PROGRAM_ID}"'" and status eq "Awaiting Approval"'
For each task that passes the agent’s validation criteria, submit an approval:
Request Body:
{
"action": "APPROVE",
"comment": "Auto-approved: value within threshold, notes present, supporting attachment verified."
}
curl -X POST "https://api.app.wdesk.com/tasks/${TASK_ID}/actionSubmission" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
For tasks that fail validation:
Request Body:
{
"action": "REJECT",
"comment": "Auto-rejected: missing supporting attachment. Please attach source documentation before resubmitting."
}
curl -X POST "https://api.app.wdesk.com/tasks/${TASK_ID}/actionSubmission" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-H "X-Version: 2026-01-01" \
-d @request.json
Tip: When using an agent as a reviewer, always include a descriptive
commentexplaining the rationale for the approval or rejection. This creates an audit trail that satisfies compliance requirements.
Common Issues#
Issue |
Solution |
|---|---|
“Duplicate program name (409 Conflict)” |
Program names must be unique within a workspace. Use |
“Batch upsert failed, all values rolled back” |
If any single value in a batch fails validation, the entire batch is rejected. Check that all coordinates reference valid dimension value IDs, all reporting periods are valid, and the payload is under 10 MB. |
“Cannot set status to complete” |
Status can only transition to |
“Dimension values list is null” |
When listing dimensions via |
“Coordinates don’t match, value not found” |
Coordinates use the dimension UUID as the key and the dimension value’s user-entered |
“Metric value |
When upserting or deleting values, you can identify a value by its |
“Polling too frequently (429 Too Many Requests)” |
Respect the |
“Dimension value ID too long” |
Dimension value |
“PATCH only allows one property at a time” |
Both program and metric PATCH endpoints support only one operation per request. Make separate PATCH requests for each property you need to update. |
“Can’t find tasks for my program” |
Use |
Error Responses#
All sustainability endpoints return standard error responses:
Status |
Code |
Meaning |
|---|---|---|
400 |
|
The request body is malformed or a required parameter is missing. |
401 |
|
The OAuth 2.0 token is missing, expired, or invalid. |
403 |
|
The token lacks the required scope or the user lacks permission on the program. |
404 |
|
The program, metric, dimension, or value ID does not exist. |
409 |
|
A resource with the same identifying attributes already exists (e.g., duplicate program name). |
429 |
|
Rate limit exceeded. Back off and retry after the period indicated in the response. |
500 |
|
The server encountered an unexpected condition. Retry the request after a brief delay. |
503 |
|
The server is temporarily unable to handle the request due to overload or maintenance. |
Error response bodies include a code and message:
{
"code": "400BadRequest",
"message": "The request was unacceptable, often due to a missing or invalid parameter"
}
Summary of API Endpoints#
Operation |
Method |
Endpoint |
Required Scope |
|---|---|---|---|
List programs |
|
|
|
Retrieve a program |
|
|
|
Create a program |
|
|
|
Update a program |
|
|
|
List topics |
|
|
|
Create a topic |
|
|
|
Update a topic |
|
|
|
Delete a topic |
|
|
|
List dimensions |
|
|
|
Retrieve a dimension |
|
|
|
Create a dimension |
|
|
|
Update a dimension |
|
|
|
List metrics |
|
|
|
Retrieve a metric |
|
|
|
Create a metric |
|
|
|
Update a metric |
|
|
|
Delete a metric |
|
|
|
List metric values |
|
|
|
Retrieve a metric value |
|
|
|
Create a metric value |
|
|
|
Update a metric value |
|
|
|
Delete a metric value |
|
|
|
Batch upsert metric values |
|
|
|
Batch upsert results |
|
|
|
Batch delete metric values |
|
|
|
Retrieve program permissions |
|
|
|
Modify program permissions |
|
|
|
List tasks |
|
|
|
Retrieve a task |
|
|
|
Submit task action |
|
|
|