Skip to main content

Push Measurement API

Audience: Admin, Developer Page Type: How-To + Reference Plan Feature: kpi_push_measurement (must be enabled for your tenant)


Overview​

The Push Measurement API allows external systems to send KPI measurements directly to COS via HTTP requests authenticated with API keys. Unlike the Pull model (where COS fetches data from your APIs), the Push model lets your CI/CD pipelines, cron jobs, or applications send measurements whenever they're ready.

How It Works​

External System                          COS
───────────────── ─────────────

Your App / CI/CD ──POST──► /integrations/push/measurement
(with x-api-key) │
├─ Validate API key
├─ Verify KPI ownership
└─ Write measurement

Prerequisites​

  1. Plan Feature: kpi_push_measurement must be enabled
  2. API Key: At least one active API key for your tenant
  3. Target ID (kpiId): The ID of a KPI in the KPI register (Goals & Tracking → KPI Tracking, KPI detail → Integration tab) or the ID of an OKR key result
  4. Target type (kpiType): KPI or OKR_KEY_RESULT
Breaking change (0.5.0, measurement date)

periodStart, periodEnd and periodicity were removed; sending them returns 400 MEASUREMENT_PERIOD_FIELDS_REMOVED. For the KPI target measurementDate (YYYY-MM-DD) is required; there is no "current month" default. An existing record on the same date is updated. The OKR_KEY_RESULT target updates the key result's actual value and takes no date.

Breaking change (KPI register)

The kpiType contract has been simplified. Only two values are accepted:

TypeTarget
KPIA KPI in the KPI register (/kpis). Because reports use the same KPI, the measurement appears in every report that uses it.
OKR_KEY_RESULTAn OKR key result

The former family values (THEME_KPI, BSC_KPI, ASIS_KPI, SWOT_KPI, PESTLE_KPI, KPI_DEFINITION, PRODUCT_SCORECARD_KPI, etc.) now return 400; there is no compatibility layer. Former per-report KPI IDs are no longer valid. Update your integrations to use the KPI register ID and kpiType: "KPI".

KPIs of every report type (including Porter and BCG) can receive pushes through the KPI register.


Managing API Keys​

Create an API Key​

Navigate to Settings → API Keys in the COS sidebar, or go to /platform/api-keys.

  1. Click New API Key
  2. Enter a descriptive name (e.g., "CI/CD Pipeline", "Monitoring System")
  3. Optionally set an expiration date
  4. Click Create
  5. Copy the key immediately — it will not be shown again
warning

API keys are only displayed once at creation time. Store the key securely (e.g., in a secrets manager or environment variable). If you lose the key, you must create a new one.

Revoke an API Key​

If a key is compromised or no longer needed, click the delete button on the key row and confirm. Revoked keys cannot be reactivated.

API key limit​

The number of keys is subject to the effective max_push_api_keys entitlement. A preset name alone does not specify a fixed number. If the limit is reached, revoke an unused key or review the effective limit with your administrator. Revocation blocks subsequent calls using that key.


Sending Measurements​

Replace https://your-api-host.example with your deployment's configured API base URL. Do not append /api to the web application origin automatically; include that prefix only if it is part of your API configuration. The Push API section in the KPI detail fills in the correct API URL, the KPI ID and the kpiType. The key supplies tenant context, so the request does not need an extra tenant or report ID.

Single Measurement​

curl -X POST https://your-api-host.example/integrations/push/measurement \
-H "x-api-key: cos_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"kpiId": "kpi-id-here",
"kpiType": "KPI",
"measurementDate": "2026-09-30",
"actual": 85.5,
"target": 100
}'

Success response: {"success": true, "kpiType": "KPI", "kpiId": "...", "measurementDate": "2026-09-30", "operation": "CREATED", "recordId": "..."}. An existing record on the same date is updated and operation: "UPDATED" is returned.

Batch Measurements (up to 50)​

curl -X POST https://your-api-host.example/integrations/push/measurements/batch \
-H "x-api-key: cos_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"measurements": [
{
"kpiId": "kpi-id-1",
"kpiType": "KPI",
"measurementDate": "2026-09-30",
"actual": 85.5,
"target": 100
},
{
"kpiId": "key-result-id-2",
"kpiType": "OKR_KEY_RESULT",
"actual": 42
}
]
}'

Request Fields​

FieldTypeRequiredDescription
kpiIdstring✅KPI register ID or key result ID; not a library item ID
kpiTypestring✅KPI or OKR_KEY_RESULT
measurementDatestring✅ (KPI)Measurement date, YYYY-MM-DD (no time zone). A KPI has one record per date; sending the same date again updates it. Not sent for OKR_KEY_RESULT (400 FIELD_NOT_SUPPORTED_FOR_KEY_RESULT).
actualnumber✅The actual value. Must be a finite JSON number; a string such as "85.5" returns 400. For a key result it updates the "Actual" field; progress is calculated as for manual entry.
targetnumber❌Target value; if omitted, null, or blank, inherits the target of the same KPI's closest measurement dated before measurementDate. Explicit 0 is preserved. Defaults to 0 without a usable previous target.
forecastnumber❌Forecast value (KPI only). For a key result: 400 FIELD_NOT_SUPPORTED_FOR_KEY_RESULT.
statusNotestring❌Status note
idempotencyKeystring❌KPI only; required for a categorized push. Reuse a key only for an identical request.
configurationVersion, categoryValues❌Categorized KPI only. With "Sum of categories" the server computes the total and every active category must be sent (400 CATEGORY_VALUES_INCOMPLETE); with "Manually entered total" the actual total is required (400 MANUAL_TOTAL_REQUIRED).

A key result body accepts only {kpiType, kpiId, actual, target?, statusNote?}; the response carries operation: "UPDATED" and the current progress. In an approved OKR plan a target different from the stored value returns 403 OKR_LOCKED; the actual value stays writable.


Code Examples​

Python​

import requests

API_KEY = "cos_your_api_key_here"
BASE_URL = "https://your-api-host.example"

response = requests.post(
f"{BASE_URL}/integrations/push/measurement",
headers={
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
json={
"kpiId": "kpi-id-here",
"kpiType": "KPI",
"measurementDate": "2026-09-30",
"actual": 85.5,
"target": 100,
},
)

print(response.status_code, response.json())

JavaScript / Node.js​

const API_KEY = 'cos_your_api_key_here';
const BASE_URL = 'https://your-api-host.example';

const response = await fetch(`${BASE_URL}/integrations/push/measurement`, {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
kpiId: 'kpi-id-here',
kpiType: 'KPI',
measurementDate: '2026-09-30',
actual: 85.5,
target: 100,
}),
});

const data = await response.json();
console.log(data);

Authentication​

All Push API requests must include the x-api-key header with a valid, active API key.

The endpoint accepts external HTTP clients, but anonymous requests and a login JWT alone cannot push measurements. A permitted user signs in to COS, opens Settings → API Keys, creates a key, and gives that key to the external system. The key fixes the tenant context: it cannot write another tenant's KPI. The tenant's kpi_push_measurement plan feature must be enabled.

Target inheritance uses the exact kpiType and kpiId target, never a shared library item or another KPI. The previous measurement is the closest one dated before the sent measurementDate. The screen prefills this target and permits editing. See the request fields and target defaults.

Error Responses​

Single request (/integrations/push/measurement): a rejected request returns an HTTP error and the body carries a machine-readable errorCode. Clients that check res.ok never treat a rejected measurement as accepted.

StatuserrorCodeMeaning
401—Missing or invalid API key
403— / FORBIDDENKey revoked or expired, tenant inactive or plan feature disabled; (when the switch is on) the target has no active binding; a target change in an approved OKR plan (OKR_LOCKED) or an inactive unit
400INVALIDInvalid body, a string instead of a number, or unsupported kpiType (including the former family values)
400MEASUREMENT_PERIOD_FIELDS_REMOVEDperiodStart, periodEnd or periodicity was sent
400FIELD_NOT_SUPPORTED_FOR_KEY_RESULTmeasurementDate, forecast or categoryValues sent for a key result
400CATEGORY_VALUES_INCOMPLETE / MANUAL_TOTAL_REQUIREDCategorized push with a missing category value / missing manual total
404NOT_FOUNDKPI or key result not found or not in the key's tenant
409KPI_RETIREDThe KPI is retired; measurements are not accepted
409CONFLICTIdempotency key or breakdown version conflict
422CYCLE_LOCKEDThe key result's OKR cycle is closed (CLOSED/COMPLETED)
429—Rate limit exceeded

Batch request (/integrations/push/measurements/batch, up to 50): every item is processed separately. The response is 200 with total, succeeded, failed and per-item results. A failed item has success: false, an errorCode (INVALID, NOT_FOUND, KPI_RETIRED, CYCLE_LOCKED, CONFLICT, FORBIDDEN, ERROR) and a message; the other items are still written.

{
"total": 2, "succeeded": 1, "failed": 1,
"results": [
{ "success": true, "kpiType": "KPI", "kpiId": "kpi-id-1", "measurementDate": "2026-09-30", "operation": "CREATED", "recordId": "..." },
{ "success": false, "kpiType": "OKR_KEY_RESULT", "kpiId": "key-result-id-2", "errorCode": "CYCLE_LOCKED", "error": "..." }
]
}

Rules​

  • Push is not affected by closed reports or a closed planning period that use the KPI; an active KPI can always be written.
  • A retired KPI cannot be written (KPI_RETIRED).
  • A key result in a closed OKR cycle cannot be written (CYCLE_LOCKED).
  • Every write appears in the KPI's History tab with the Push API channel and the API key ID.
  • Binding requirement: By default a push is accepted for any active KPI in the key's tenant, even without an integration binding. If the installation administrator enables PUSH_REQUIRE_ACTIVE_BINDING, a push is accepted only when the target KPI or key result has an active binding; otherwise 403 FORBIDDEN is returned. The default of this switch is under product review; ask your administrator for your installation's value.

Security Notes​

  • API keys are hashed (SHA-256) before storage — COS never stores raw keys
  • Keys are identified by a 8-character prefix (cos_xxxx) for management
  • lastUsedAt is updated on each successful request
  • Set expiration dates for rotating keys on a schedule
  • Revoke keys immediately if compromised

In-App Push Snippet​

In the Integration tab of a KPI in KPI Tracking, or in the KPI detail drawer of a report, the Push API section (visible when kpi_push_measurement is enabled) provides ready-to-use code snippets with the KPI ID and kpiType: "KPI" pre-filled. Just replace YOUR_API_KEY with your actual key.


Push vs Pull Comparison​

AspectPush APIPull Integration
DirectionExternal → COSCOS → External
AuthAPI Key (x-api-key)JWT + configured endpoint auth
ScheduleOn-demand (caller decides)Cron-based (COS scheduler)
SetupCreate API key, send HTTP POSTConfigure endpoint + binding
Plan Featurekpi_push_measurementkpi_api_integration
Best ForCI/CD, event-driven, webhooksPeriodic polling of external APIs