API reference

The metrics ingest endpoint accepts one or many metric data points via a single POST request. Authenticated by a workspace API key.

Balcony exposes one public endpoint for programmatic metric ingestion. It accepts individual data points or batches of up to 100 points in a single request.

Authentication

All requests require a workspace API key passed as a Bearer token:

Authorization: Bearer <your-api-key>

API keys are workspace-scoped and can be created in your workspace settings. A key that is invalid, expired, or belongs to a different workspace returns a 401.

Ingest metric data points

POST /api/v1/metrics

Accepts a single metric data point or an array of up to 100 points.

Request body

Single point:

{
  "metric_id": "3f2d1b8a-...",
  "value": 94.2,
  "timestamp": "2025-11-01T09:00:00Z",
  "origin": "monitoring-pipeline-v2"
}

Batch (array):

[
  {
    "metric_id": "3f2d1b8a-...",
    "value": 94.2,
    "timestamp": "2025-11-01T09:00:00Z"
  },
  {
    "metric_id": "7c4e9f0d-...",
    "value": 12
  }
]

Fields

FieldTypeRequiredDescription
metric_idstring (UUID)YesThe ID of the target metric. Must belong to a mechanism in the authenticated workspace.
valuenumberYesA finite numeric value.
timestampstring (ISO 8601)NoThe time the value was recorded. Defaults to the time of ingestion if omitted.
originstringNoA label identifying the data source — useful for tracing where a value came from.

How to find a metric ID

Metric IDs are available in the mechanism editor. Open a mechanism, navigate to the Metrics section, and copy the ID from the metric's detail view.

Limits

  • Maximum 100 points per request.
  • All metric_id values in a request must belong to the authenticated workspace. A single invalid or inaccessible ID in a batch causes the entire request to fail with a 404.

Success response

{ "ingested": 3 }

Returns a 200 with the count of successfully ingested points.

Error responses

401 — Missing or invalid API key

{ "error": "Missing API key" }
{ "error": "Invalid or expired API key" }

400 — Validation failure

{
  "error": "Validation failed",
  "details": [
    "[1] metric_id is required",
    "[2] value must be a finite number"
  ]
}

400 — Batch too large

{ "error": "Maximum 100 points per request" }

404 — Unknown metric IDs

{
  "error": "One or more metric IDs not found or not accessible",
  "invalid": ["7c4e9f0d-..."]
}

500 — Server error

{ "error": "Failed to insert metric points" }

Example: curl

curl -X POST https://balcony.academy/api/v1/metrics \
  -H "Authorization: Bearer bky_..." \
  -H "Content-Type: application/json" \
  -d '[
    {
      "metric_id": "3f2d1b8a-0000-0000-0000-000000000001",
      "value": 94.2,
      "timestamp": "2025-11-01T09:00:00Z",
      "origin": "risk-scoring-pipeline"
    }
  ]'

Example: JavaScript

const response = await fetch('/api/v1/metrics', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify([
    {
      metric_id: '3f2d1b8a-0000-0000-0000-000000000001',
      value: 94.2,
      timestamp: new Date().toISOString(),
      origin: 'risk-scoring-pipeline',
    },
  ]),
});

const result = await response.json();
// { ingested: 1 }