---
title: Endpoints · API reference
url: https://docs.schemalabs.ai/api-reference/endpoints
description: Create, serve, refresh, upgrade, and delete endpoints: a base plus pinned data, a configuration, system prompts, and a held-out report at a stable URL.
---

# Endpoints

> Create, serve, refresh, upgrade, and delete endpoints: a base plus pinned data, a configuration, system prompts, and a held-out report at a stable URL.

An endpoint is the persistent product object: a base model plus pinned dataset snapshots, a configuration (target, task, thresholds), a library of system prompts, and a held-out report, served at a stable URL. Creation pins the data, evaluates held-out, and exposes the URL, in minutes.

The endpoint ID is an immutable unique ID; the `name` is a mutable alias. Every route accepts either, but only the id is stable across a rename. `refresh` and `upgrade` change what the endpoint serves behind the same URL, and every create, refresh, and upgrade mints an immutable held-out report.

## The endpoint object

Returned by create (on completion), retrieve, and list. Retrieve also carries the bundle already computed over the pinned data, with cursor pagination for large arrays. Reading the object runs no inference.

**Attributes**

- `id` (string): Immutable unique ID. Surfaces display its first 8 characters; the full ID is the identifier.
- `endpoint` (string): Mutable display name (lowercase slug), for example `churn`.
- `status` (string): Lifecycle state. The endpoint keeps serving during refresh and upgrade; watch progress on the job. One of: `creating`, `live`.
- `url` (string): The serve URL, `https://api.schemalabs.ai/v2/serve/{id}`. Never changes. `serve_url` is the relative form.
- `base` (string): The Schema model currently served, for example `schema-2`.
- `feed` (string): Whether the endpoint pins one table or several. One of: `single_table`, `multi_table`.
- `created_at` (string): Creation time.
- `calls` (integer): Serve calls made against this endpoint.
- `data` (object): Pinned data: `datasets[]` (each `dataset` `ds_...`, `name`, `pinned`, `snapshot` `dsv_...`), plus the top-level `snapshot` and `tables` (dataset ids).
- `config_revision` (integer): Increments on every configuration edit (target, task, thresholds, system prompts).
- `data_revision` (integer): Increments on every data re-run (refresh). Independent of `config_revision`.
- `system_prompts` (array of objects): The endpoint’s system-prompt fragments: `id` (`sp_...`), `name`, `active`, `order`, `body`. Read them here to apply in your own LLM; Chat applies them on the platform.
- `report` (string): Display name of the current held-out report, `{endpoint}.{base}.{op}.{YYYY-MM-DD}`.
- `report_id` (string): Immutable id of the current report, `r_...`.
- `held_out` (object): The current held-out block: `score`, `metric`, task metrics (`auc`, `per_class`, `rmse`, `nrmse`, `quantile_calibration`, ...), the baseline (`baseline_majority` or `baseline_mean`), `split { method, train_rows, test_rows, seed, unit }`, `data_version`, `data_snapshot`, `weakest_slice`, `low_confidence` with `low_confidence_reason`, an estimate `note`, and `report_url`.
- `summary` (object): Headline counts: `tables`, `records_linked`, `records_total`, `keys_used` (`"none"`: no shared key was used to relate the tables), `shared_attributes`, and the `prediction` headline (`target`, `task`, `tier`, `held_out`).
- `tables` (array of objects): One entry per input table.
  - `id` (string): The table id you supplied.
  - `rows` (integer): Row count.
  - `cols` (integer): Column count.
  - `sector` (object): Vertical-agnostic sector identification from cell values alone, any domain, no metadata: `top1 { name, confidence }` and `top5[]`, each with its own confidence.
  - `column_profile` (array of objects): Per column: `name`, `role` (email, phone, date, name, code, measure, ...), `role_confidence`, `type` (categorical, numeric, datetime, text, boolean), `missing_pct`, `pii` (boolean).
- `cross_table_map` (object | null): Present on two or more tables. `summary`, `column_alignment[]` (each with `attribute`, `confidence`, and a format-invariance `example`), `entity_matches[]` (row pairs, `confidence`, `matched_on`), and `unified_schema` (`shared`, `a_only`, `b_only`). Every alignment and match carries its confidence and evidence.
- `unified_rows` (object | null): The cross-table map materialized into joined records: `schema`, `provenance`, `rows[]` (each with `entity`, `from`, `confidence`, `record`, and `shared_variants` showing each source’s raw value), `total`, and `sample_shown`. Paginated by cursor.
- `imputation` (object): Missing-value imputation: `filled[]` (table, row, column, value, method), `row_confidence[]` (table, row, confidence), and `total_filled`. Confidence is row-level, not per cell.
- `target_selection` (object): `mode` (`auto`, `user`, `none`), `column`, `reason`, `overridable`, and `candidates[]` (per column: `selected`, `eligible`, and a plain-language `reason`). `null` candidates when the target was user-specified.
- `task_selection` (object): `mode`, `type`, `reason`, `overridable`.
- `prediction` (object | null): The prediction slice, shaped by task type: classification (`label`, `confidence`, `probabilities`), regression (`value`, `median`, `std`, `quantiles`), anomaly (`anomaly_score`, `is_anomaly`, `threshold`). `tier` is `in_context`. `dropped_rows.missing_target` counts rows excluded because their target was missing. `null` when `target.mode` is `none`.

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "status": "live",
  "url": "https://api.schemalabs.ai/v2/serve/{endpoint_id}",
  "serve_url": "/v2/serve/{endpoint_id}",
  "base": "schema-2",
  "feed": "multi_table",
  "created_at": "2026-03-10T14:07:02Z",
  "calls": 128,
  "data": {
    "datasets": [
      {
        "dataset": "ds_crm01",
        "name": "crm_contacts",
        "pinned": true,
        "snapshot": "dsv_91f2"
      },
      {
        "dataset": "ds_bill01",
        "name": "billing_db",
        "pinned": true,
        "snapshot": "dsv_c4e7"
      }
    ],
    "pinned": true,
    "snapshot": "dsv_91f2",
    "tables": [
      "ds_crm01",
      "ds_bill01"
    ]
  },
  "config_revision": 1,
  "data_revision": 2,
  "system_prompts": [
    {
      "id": "sp_12",
      "name": "default",
      "active": true,
      "order": 1,
      "body": "You are a data assistant for this endpoint. Answer from the endpoint outputs."
    }
  ],
  "report": "churn.s2.upgrade.2026-08-14",
  "report_id": "r_a7d0",
  "summary": {
    "tables": 2,
    "records_linked": 21240,
    "records_total": 22400,
    "keys_used": "none",
    "prediction": {
      "target": "crm_contacts.c3",
      "task": "classification",
      "held_out": 0.961
    }
  },
  "tables": [
    {
      "id": "crm_contacts",
      "rows": 22400,
      "cols": 12,
      "sector": {
        "top1": {
          "name": "subscription software",
          "confidence": 0.88
        },
        "top5": [
          {
            "name": "subscription software",
            "confidence": 0.88
          },
          {
            "name": "customer relationship management",
            "confidence": 0.76
          },
          {
            "name": "telecommunications",
            "confidence": 0.52
          },
          {
            "name": "consumer financial services",
            "confidence": 0.41
          },
          {
            "name": "marketing services",
            "confidence": 0.3
          }
        ]
      },
      "column_profile": [
        {
          "name": "c0",
          "role": "email",
          "role_confidence": 0.98,
          "type": "categorical",
          "missing_pct": 0,
          "pii": true
        },
        {
          "name": "c1",
          "role": "phone",
          "role_confidence": 0.97,
          "type": "categorical",
          "missing_pct": 0.01,
          "pii": true
        },
        {
          "name": "c2",
          "role": "date",
          "role_confidence": 0.95,
          "type": "datetime",
          "missing_pct": 0.02,
          "pii": false
        },
        {
          "name": "c3",
          "role": "category",
          "role_confidence": 0.92,
          "type": "categorical",
          "missing_pct": 0,
          "pii": false
        }
      ]
    },
    {
      "id": "billing_db",
      "rows": 22400,
      "cols": 6,
      "sector": {
        "top1": {
          "name": "subscription billing",
          "confidence": 0.9
        },
        "top5": [
          {
            "name": "subscription billing",
            "confidence": 0.9
          },
          {
            "name": "subscription software",
            "confidence": 0.71
          },
          {
            "name": "payments processing",
            "confidence": 0.58
          },
          {
            "name": "accounting",
            "confidence": 0.36
          },
          {
            "name": "telecommunications",
            "confidence": 0.29
          }
        ]
      },
      "column_profile": [
        {
          "name": "c0",
          "role": "code",
          "role_confidence": 0.94,
          "type": "categorical",
          "missing_pct": 0,
          "pii": false
        },
        {
          "name": "c1",
          "role": "phone",
          "role_confidence": 0.96,
          "type": "categorical",
          "missing_pct": 0.01,
          "pii": true
        },
        {
          "name": "c2",
          "role": "measure",
          "role_confidence": 0.97,
          "type": "numeric",
          "missing_pct": 0.03,
          "pii": false
        }
      ]
    }
  ],
  "cross_table_map": {
    "summary": {
      "tables": 2,
      "shared_attributes": 1,
      "entities_matched": 21240,
      "columns_only_in_a": 11,
      "columns_only_in_b": 5,
      "keys_used": "none"
    },
    "column_alignment": [
      {
        "a": "crm_contacts.c1",
        "b": "billing_db.c1",
        "attribute": "phone",
        "confidence": 0.96,
        "example": {
          "a": "555-0142",
          "b": "+1 555 0142"
        }
      }
    ],
    "entity_matches": [
      {
        "a_row": 0,
        "b_row": 0,
        "confidence": 0.94,
        "matched_on": [
          "phone"
        ]
      }
    ],
    "unified_schema": {
      "shared": [
        {
          "attribute": "phone",
          "from": [
            "crm_contacts.c1",
            "billing_db.c1"
          ]
        }
      ],
      "a_only": [
        "crm_contacts.c0 (email)",
        "crm_contacts.c2 (date)"
      ],
      "b_only": [
        "billing_db.c2 (measure)"
      ]
    }
  },
  "unified_rows": {
    "schema": [
      "email",
      "phone",
      "signup_date",
      "monthly_spend"
    ],
    "rows": [
      {
        "entity": 0,
        "from": {
          "a_row": 0,
          "b_row": 0
        },
        "confidence": 0.94,
        "record": {
          "email": "ana@example.io",
          "phone": "555-0142",
          "signup_date": "2025-01-10",
          "monthly_spend": 49
        },
        "shared_variants": {
          "phone": {
            "crm_contacts.c1": "555-0142",
            "billing_db.c1": "+1 555 0142"
          }
        }
      }
    ],
    "total": 21240,
    "sample_shown": 1
  },
  "imputation": {
    "filled": [
      {
        "table": "billing_db",
        "row": 17,
        "column": "c2",
        "value": 49,
        "method": "schema-2"
      }
    ],
    "row_confidence": [
      {
        "table": "billing_db",
        "row": 17,
        "confidence": 0.83
      }
    ],
    "total_filled": 812
  },
  "target_selection": {
    "mode": "auto",
    "column": "crm_contacts.c3",
    "overridable": true,
    "reason": "most predictable target among the eligible categorical columns"
  },
  "task_selection": {
    "mode": "auto",
    "type": "classification",
    "reason": "categorical target",
    "overridable": true
  },
  "prediction": {
    "target": "crm_contacts.c3",
    "task_type": "classification",
    "tier": "in_context",
    "classes": [
      "retained",
      "churned"
    ],
    "results": [
      {
        "row": 0,
        "label": "retained",
        "confidence": 0.95,
        "probabilities": {
          "retained": 0.95,
          "churned": 0.05
        }
      }
    ]
  },
  "held_out": {
    "score": 0.961,
    "metric": "accuracy",
    "auc": 0.974,
    "baseline_majority": 0.71,
    "per_class": {
      "retained": 0.97,
      "churned": 0.91
    },
    "split": {
      "method": "in_context_holdout",
      "train_rows": 17920,
      "test_rows": 4480,
      "seed": 7,
      "unit": "entity"
    },
    "data_version": "2026-08-12 09:31",
    "data_snapshot": "dsv_91f2",
    "weakest_slice": "tenure<3mo: 0.84",
    "low_confidence": null,
    "low_confidence_reason": null,
    "note": "estimate of the live endpoint; serving uses the full pinned snapshot",
    "report_url": "/v2/reports/r_a7d0"
  }
}
```

## Operations

### Create an endpoint

`POST /v2/endpoints` (scope: `manage`)

Publishes a configuration as an endpoint: pins the referenced data, evaluates held-out by in-context split, and exposes a stable URL. Returns `202` and a job.

The endpoint id is assigned at submit time, so it appears in `GET /v2/endpoints` immediately with `status: "creating"`, then flips to `live` when the job completes. Inline `tables` are accepted and are ingested as an upload-type dataset first.

A new endpoint starts with one default system-prompt fragment. Its library is edited on the endpoint page in the platform.

**Headers**

- `Idempotency-Key` (string): Optional. A unique key for this request. Retrying a POST with the same key and body never creates a duplicate job; the same key with a different body returns `409 conflict`.

**Body** (application/json)

- `name` (string, required): Display name, a lowercase slug such as `churn`. Mutable; the id is what stays stable.
- `base` (string, default latest): The Schema model to create on; defaults to the latest (see `GET /v2/models`). Switch later with `upgrade`.
- `data` (array of strings): Dataset ids and connection refs to pin. Datasets referenced here are pinned automatically and stay pinned while the endpoint serves. Required unless `tables` is supplied.
- `tables` (array of objects): Inline tables. Ingested as an upload dataset (visible under Data) before pinning. Required unless `data` is supplied.
  - `id` (string, required): Your name for the table. Appears in every per-table output and in cross-table references such as `crm_contacts.c1`.
  - `columns` (array of strings, required): Column names. They may be real names, opaque labels (`c0`, `c1`), or empty: Schema understands the values, not the names.
  - `rows` (array of arrays, required): Row values in column order. Use `null` for missing cells; missing cells are metered like any other cell.
- `target` (object, default { "mode": "auto" }): Which column to predict. `auto` lets Schema choose the most predictable eligible column and explains why; `column` names one; `none` returns an understanding-only bundle with no prediction slice.
  - `mode` (string): `auto` (default) or `none`. Omit when supplying `column`. One of: `auto`, `none`.
  - `column` (string): A column name, qualified as `table.column` on multi-table input (for example `cards_db.card_brand`).
- `task` (object, default { "mode": "auto" }): Prediction task type. `auto` infers it from the target (categorical → classification, numeric → regression). Anomaly detection runs on explicit request: set `type: "anomaly"`, which works without a target.
  - `mode` (string): Infer the task from the target. One of: `auto`.
  - `type` (string): Explicit task type. Set `regression` together with a numeric `target.column`; `anomaly` runs unsupervised and needs no target. One of: `classification`, `regression`, `anomaly`.
- `options` (object): Per-request options.
  - `confidence_threshold` (number): Minimum confidence for cross-table column alignments and entity matches to be reported. Omit to use the service default.

**Returns**

`202 Accepted` with the endpoint id, name, `status: "creating"`, and the job. Poll the job, or retrieve the endpoint until `status` is `live`.

**Response fields**

- `endpoint` (string): The name you supplied.
- `id` (string): The endpoint ID, final from this moment.
- `status` (string): `creating`.
- `job_id` (string): The creation job.

**Example request (cURL)**

```bash
curl -X POST 'https://api.schemalabs.ai/v2/endpoints' \
  -H "Authorization: Bearer $SCHEMA_API_KEY" \
  -H "Idempotency-Key: <unique key>" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "churn",
  "base": "schema-2",
  "data": ["ds_crm01", "ds_bill01"],
  "target": { "mode": "auto" },
  "task": { "mode": "auto" }
}'
```

**Example response (202)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "status": "creating",
  "job_id": "job_9a5b01d4"
}
```

> Metering: one fresh pass over the pinned snapshot at the fresh rate, plus the storage line for pinned data. The held-out evaluation is included at no extra charge.

> `options.processing` and `options.out` apply to quick runs and synthetic generation.

### List endpoints

`GET /v2/endpoints` (scope: `read`)

Lists your organization’s endpoints, newest first, including ones still creating.

**Query parameters**

- `limit` (integer, default 50): Page size.
- `cursor` (string): Cursor from a previous page.

**Returns**

A page of endpoint objects (summary fields, without the bundle) and a `next_cursor`.

**Example request (cURL)**

```bash
curl 'https://api.schemalabs.ai/v2/endpoints?limit=20' \
  -H "Authorization: Bearer $SCHEMA_API_KEY"
```

**Example response (200)**

```json
{
  "endpoints": [
    {
      "endpoint": "churn",
      "id": "{endpoint_id}",
      "status": "live",
      "base": "schema-2",
      "data": {
        "datasets": [
          {
            "dataset": "ds_crm01",
            "name": "crm_contacts",
            "pinned": true,
            "snapshot": "dsv_91f2"
          },
          {
            "dataset": "ds_bill01",
            "name": "billing_db",
            "pinned": true,
            "snapshot": "dsv_c4e7"
          }
        ],
        "pinned": true,
        "snapshot": "dsv_91f2",
        "tables": [
          "ds_crm01",
          "ds_bill01"
        ]
      },
      "report_id": "r_a7d0",
      "held_out": {
        "score": 0.961,
        "metric": "accuracy"
      },
      "url": "https://api.schemalabs.ai/v2/serve/{endpoint_id}"
    },
    {
      "endpoint": "claims-severity",
      "id": "{other_endpoint_id}",
      "status": "creating",
      "base": "schema-2",
      "data": {
        "datasets": [
          {
            "dataset": "ds_claims",
            "name": "claims_2025",
            "pinned": true,
            "snapshot": null
          }
        ],
        "pinned": true,
        "snapshot": null,
        "tables": [
          "ds_claims"
        ]
      },
      "report_id": null,
      "held_out": null,
      "url": "https://api.schemalabs.ai/v2/serve/{other_endpoint_id}"
    }
  ],
  "next_cursor": null,
  "total": 2
}
```

### Retrieve an endpoint

`GET /v2/endpoints/:id` (scope: `read`)

Returns the endpoint object with its state, configuration, system prompts, current report, and the bundle already computed over the pinned data. Runs no inference.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Query parameters**

- `cursor` (string): Cursor for the large arrays (`unified_rows.rows`, `prediction.results`, `imputation.filled`).
- `limit` (integer, default 100): Rows per page for the large arrays.

**Returns**

The [endpoint object](#the-endpoint-object).

**Example request (cURL)**

```bash
curl 'https://api.schemalabs.ai/v2/endpoints/{endpoint_id}' \
  -H "Authorization: Bearer $SCHEMA_API_KEY"
```

**Example response (200)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "status": "live",
  "url": "https://api.schemalabs.ai/v2/serve/{endpoint_id}",
  "serve_url": "/v2/serve/{endpoint_id}",
  "base": "schema-2",
  "feed": "multi_table",
  "created_at": "2026-03-10T14:07:02Z",
  "calls": 128,
  "data": {
    "datasets": [
      {
        "dataset": "ds_crm01",
        "name": "crm_contacts",
        "pinned": true,
        "snapshot": "dsv_91f2"
      },
      {
        "dataset": "ds_bill01",
        "name": "billing_db",
        "pinned": true,
        "snapshot": "dsv_c4e7"
      }
    ],
    "pinned": true,
    "snapshot": "dsv_91f2",
    "tables": [
      "ds_crm01",
      "ds_bill01"
    ]
  },
  "config_revision": 1,
  "data_revision": 2,
  "system_prompts": [
    {
      "id": "sp_12",
      "name": "default",
      "active": true,
      "order": 1,
      "body": "You are a data assistant for this endpoint. Answer from the endpoint outputs."
    }
  ],
  "report": "churn.s2.upgrade.2026-08-14",
  "report_id": "r_a7d0",
  "summary": {
    "tables": 2,
    "records_linked": 21240,
    "records_total": 22400,
    "keys_used": "none",
    "prediction": {
      "target": "crm_contacts.c3",
      "task": "classification",
      "held_out": 0.961
    }
  },
  "tables": [
    {
      "id": "crm_contacts",
      "rows": 22400,
      "cols": 12,
      "sector": {
        "top1": {
          "name": "subscription software",
          "confidence": 0.88
        },
        "top5": [
          {
            "name": "subscription software",
            "confidence": 0.88
          },
          {
            "name": "customer relationship management",
            "confidence": 0.76
          },
          {
            "name": "telecommunications",
            "confidence": 0.52
          },
          {
            "name": "consumer financial services",
            "confidence": 0.41
          },
          {
            "name": "marketing services",
            "confidence": 0.3
          }
        ]
      },
      "column_profile": [
        {
          "name": "c0",
          "role": "email",
          "role_confidence": 0.98,
          "type": "categorical",
          "missing_pct": 0,
          "pii": true
        },
        {
          "name": "c1",
          "role": "phone",
          "role_confidence": 0.97,
          "type": "categorical",
          "missing_pct": 0.01,
          "pii": true
        },
        {
          "name": "c2",
          "role": "date",
          "role_confidence": 0.95,
          "type": "datetime",
          "missing_pct": 0.02,
          "pii": false
        },
        {
          "name": "c3",
          "role": "category",
          "role_confidence": 0.92,
          "type": "categorical",
          "missing_pct": 0,
          "pii": false
        }
      ]
    },
    {
      "id": "billing_db",
      "rows": 22400,
      "cols": 6,
      "sector": {
        "top1": {
          "name": "subscription billing",
          "confidence": 0.9
        },
        "top5": [
          {
            "name": "subscription billing",
            "confidence": 0.9
          },
          {
            "name": "subscription software",
            "confidence": 0.71
          },
          {
            "name": "payments processing",
            "confidence": 0.58
          },
          {
            "name": "accounting",
            "confidence": 0.36
          },
          {
            "name": "telecommunications",
            "confidence": 0.29
          }
        ]
      },
      "column_profile": [
        {
          "name": "c0",
          "role": "code",
          "role_confidence": 0.94,
          "type": "categorical",
          "missing_pct": 0,
          "pii": false
        },
        {
          "name": "c1",
          "role": "phone",
          "role_confidence": 0.96,
          "type": "categorical",
          "missing_pct": 0.01,
          "pii": true
        },
        {
          "name": "c2",
          "role": "measure",
          "role_confidence": 0.97,
          "type": "numeric",
          "missing_pct": 0.03,
          "pii": false
        }
      ]
    }
  ],
  "cross_table_map": {
    "summary": {
      "tables": 2,
      "shared_attributes": 1,
      "entities_matched": 21240,
      "columns_only_in_a": 11,
      "columns_only_in_b": 5,
      "keys_used": "none"
    },
    "column_alignment": [
      {
        "a": "crm_contacts.c1",
        "b": "billing_db.c1",
        "attribute": "phone",
        "confidence": 0.96,
        "example": {
          "a": "555-0142",
          "b": "+1 555 0142"
        }
      }
    ],
    "entity_matches": [
      {
        "a_row": 0,
        "b_row": 0,
        "confidence": 0.94,
        "matched_on": [
          "phone"
        ]
      }
    ],
    "unified_schema": {
      "shared": [
        {
          "attribute": "phone",
          "from": [
            "crm_contacts.c1",
            "billing_db.c1"
          ]
        }
      ],
      "a_only": [
        "crm_contacts.c0 (email)",
        "crm_contacts.c2 (date)"
      ],
      "b_only": [
        "billing_db.c2 (measure)"
      ]
    }
  },
  "unified_rows": {
    "schema": [
      "email",
      "phone",
      "signup_date",
      "monthly_spend"
    ],
    "rows": [
      {
        "entity": 0,
        "from": {
          "a_row": 0,
          "b_row": 0
        },
        "confidence": 0.94,
        "record": {
          "email": "ana@example.io",
          "phone": "555-0142",
          "signup_date": "2025-01-10",
          "monthly_spend": 49
        },
        "shared_variants": {
          "phone": {
            "crm_contacts.c1": "555-0142",
            "billing_db.c1": "+1 555 0142"
          }
        }
      }
    ],
    "total": 21240,
    "sample_shown": 1
  },
  "imputation": {
    "filled": [
      {
        "table": "billing_db",
        "row": 17,
        "column": "c2",
        "value": 49,
        "method": "schema-2"
      }
    ],
    "row_confidence": [
      {
        "table": "billing_db",
        "row": 17,
        "confidence": 0.83
      }
    ],
    "total_filled": 812
  },
  "target_selection": {
    "mode": "auto",
    "column": "crm_contacts.c3",
    "overridable": true,
    "reason": "most predictable target among the eligible categorical columns"
  },
  "task_selection": {
    "mode": "auto",
    "type": "classification",
    "reason": "categorical target",
    "overridable": true
  },
  "prediction": {
    "target": "crm_contacts.c3",
    "task_type": "classification",
    "tier": "in_context",
    "classes": [
      "retained",
      "churned"
    ],
    "results": [
      {
        "row": 0,
        "label": "retained",
        "confidence": 0.95,
        "probabilities": {
          "retained": 0.95,
          "churned": 0.05
        }
      }
    ]
  },
  "held_out": {
    "score": 0.961,
    "metric": "accuracy",
    "auc": 0.974,
    "baseline_majority": 0.71,
    "per_class": {
      "retained": 0.97,
      "churned": 0.91
    },
    "split": {
      "method": "in_context_holdout",
      "train_rows": 17920,
      "test_rows": 4480,
      "seed": 7,
      "unit": "entity"
    },
    "data_version": "2026-08-12 09:31",
    "data_snapshot": "dsv_91f2",
    "weakest_slice": "tenure<3mo: 0.84",
    "low_confidence": null,
    "low_confidence_reason": null,
    "note": "estimate of the live endpoint; serving uses the full pinned snapshot",
    "report_url": "/v2/reports/r_a7d0"
  }
}
```

### Serve an endpoint

`POST /v2/serve/:id` (scope: `serve`)

Sends new data to a live endpoint and returns the full bundle for that data, scored against the endpoint’s pinned data.

A serve scores the request rows against the endpoint’s pinned snapshot. Request data is validated against the schema of the pinned snapshot; a mismatch returns `400 validation_error` naming the divergence.

A serve returns the bundle; narration is yours to run. The endpoint’s system prompts and current held-out report are read from the endpoint object (`GET /v2/endpoints/:id`). Production systems should use a key with `serve` alone.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Body** (application/json)

- `tables` (array of objects, required): Tables to score against the pinned data. Table ids and columns must match the pinned snapshot’s schema.
  - `id` (string, required): Your name for the table. Appears in every per-table output and in cross-table references such as `crm_contacts.c1`.
  - `columns` (array of strings, required): Column names. They may be real names, opaque labels (`c0`, `c1`), or empty: Schema understands the values, not the names.
  - `rows` (array of arrays, required): Row values in column order. Use `null` for missing cells; missing cells are metered like any other cell.
- `options` (object): `confidence_threshold` as on a run.

**Returns**

The bundle for the request data, scored by the endpoint over its pinned data. The endpoint’s held-out report and system prompts are read from the endpoint object (`GET /v2/endpoints/:id`).

**Example request (cURL)**

```bash
curl -X POST 'https://api.schemalabs.ai/v2/serve/{endpoint_id}' \
  -H "Authorization: Bearer $SCHEMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "tables": [
    {
      "id": "crm_contacts",
      "columns": ["c0", "c1", "c2", "c3"],
      "rows": [
        ["ana@example.io", "555-0142", "1990-04-02", null],
        ["lee@example.io", "555-0199", "1988-07-19", null]
      ]
    }
  ]
}'
```

**Example response (200)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "base": "schema-2",
  "mode": "serve",
  "status": "complete",
  "config_revision": 1,
  "data": {
    "pinned": true,
    "snapshot": "dsv_91f2",
    "tables": [
      "ds_crm01",
      "ds_bill01"
    ]
  },
  "summary": {
    "tables": 1,
    "keys_used": "none",
    "prediction": {
      "target": "crm_contacts.c3",
      "task": "classification",
      "tier": "in_context",
      "held_out": null
    }
  },
  "tables": [
    {
      "id": "crm_contacts",
      "rows": 2,
      "cols": 4,
      "sector": {
        "top1": {
          "name": "subscription software",
          "confidence": 0.86
        },
        "top5": [
          {
            "name": "subscription software",
            "confidence": 0.86
          },
          {
            "name": "customer relationship management",
            "confidence": 0.72
          },
          {
            "name": "telecommunications",
            "confidence": 0.49
          },
          {
            "name": "consumer financial services",
            "confidence": 0.38
          },
          {
            "name": "marketing services",
            "confidence": 0.27
          }
        ]
      },
      "column_profile": [
        {
          "name": "c0",
          "role": "email",
          "role_confidence": 0.98,
          "type": "categorical",
          "missing_pct": 0,
          "pii": true
        },
        {
          "name": "c1",
          "role": "phone",
          "role_confidence": 0.97,
          "type": "categorical",
          "missing_pct": 0,
          "pii": true
        },
        {
          "name": "c2",
          "role": "date",
          "role_confidence": 0.95,
          "type": "datetime",
          "missing_pct": 0,
          "pii": false
        },
        {
          "name": "c3",
          "role": "category",
          "role_confidence": 0.92,
          "type": "categorical",
          "missing_pct": 1,
          "pii": false
        }
      ]
    }
  ],
  "cross_table_map": null,
  "unified_rows": null,
  "imputation": {
    "filled": [],
    "row_confidence": [],
    "total_filled": 0
  },
  "prediction": {
    "target": "crm_contacts.c3",
    "task_type": "classification",
    "tier": "in_context",
    "classes": [
      "retained",
      "churned"
    ],
    "note": "scored by this endpoint over its pinned data plus the rows in this request",
    "results": [
      {
        "row": 0,
        "label": "retained",
        "confidence": 0.95,
        "probabilities": {
          "retained": 0.95,
          "churned": 0.05
        }
      },
      {
        "row": 1,
        "label": "churned",
        "confidence": 0.81,
        "probabilities": {
          "retained": 0.19,
          "churned": 0.81
        }
      }
    ]
  }
}
```

**Schema mismatch (400)**

```json
{
  "error": {
    "type": "validation_error",
    "message": "table crm_contacts: column c12 is not in the pinned snapshot dsv_91f2 (12 columns, c0..c11)",
    "param": "tables[0].columns",
    "request_id": "req_7a1c9e"
  }
}
```

> Metering: request rows bill fresh, the pinned context bills the cached rate. An idle endpoint costs nothing. Each endpoint enforces per-endpoint and per-key rate limits (429 with `Retry-After`).

### Refresh an endpoint

`POST /v2/endpoints/:id/refresh` (scope: `manage`)

Re-syncs the pinned snapshot and re-scores held-out on a fresh split; only what changed bills fresh. Mints a new report. Returns `202` and a job.

Refresh is how an endpoint follows its source. Sync the dataset first with `POST /v2/data/:id/sync`, or let refresh sync inline. Removed rows leave the pinned context immediately; it is the only operation that makes an endpoint stop reflecting deleted rows.

One mutating job runs per endpoint at a time: a second refresh or upgrade returns `409 conflict` naming the running `job_id`. Calls are unaffected while a refresh runs.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Headers**

- `Idempotency-Key` (string): Optional. A unique key for this request. Retrying a POST with the same key and body never creates a duplicate job; the same key with a different body returns `409 conflict`.

**Returns**

`202` with the job. When it completes, the endpoint object carries the new snapshot, a `diff`, and the new report.

**Example request (cURL)**

```bash
curl -X POST 'https://api.schemalabs.ai/v2/endpoints/{endpoint_id}/refresh' \
  -H "Authorization: Bearer $SCHEMA_API_KEY" \
  -H "Idempotency-Key: <unique key>"
```

**Example response (202)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "job_id": "job_5c07e3b1"
}
```

**Completed (from GET /v2/endpoints/:id), refreshed on 2026-08-12 while still on schema-1 (200)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "base": "schema-1",
  "data": {
    "datasets": [
      {
        "dataset": "ds_crm01",
        "name": "crm_contacts",
        "pinned": true,
        "snapshot": "dsv_91f2"
      },
      {
        "dataset": "ds_bill01",
        "name": "billing_db",
        "pinned": true,
        "snapshot": "dsv_c4e7"
      }
    ],
    "pinned": true,
    "snapshot": "dsv_91f2",
    "tables": [
      "ds_crm01",
      "ds_bill01"
    ]
  },
  "diff": {
    "rows_added": 310,
    "rows_changed": 12,
    "rows_removed": 4,
    "rows_unchanged": 22078
  },
  "reason": "source data changed",
  "report": "churn.s1.refresh.2026-08-12",
  "report_id": "r_b5e1",
  "held_out": {
    "score": 0.948
  }
}
```

**Mutating job already running (409)**

```json
{
  "error": {
    "type": "conflict",
    "message": "endpoint churn already has a mutating job running; wait for it to finish or cancel it",
    "running_job_id": "job_5c07e3b1",
    "request_id": "req_5d17ce"
  }
}
```

> Metering: added and changed rows bill fresh, unchanged context bills the cached rate, removals bill nothing.

### Upgrade an endpoint

`POST /v2/endpoints/:id/upgrade` (scope: `manage`)

Re-runs the same pinned data on a newer base and mints a new report. The endpoint serves the new base from completion. Returns `202` and a job.

The endpoint serves the new base from completion; the previous report stays in the trail and the delta reads on the Reports page or via the reports API.

An endpoint created on a base that has since been retired keeps that base until it is upgraded; upgrade it to resume serving. The example below moves `churn` from `schema-1` (an older base, and its base since creation on 2026-03-10) to `schema-2`.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Headers**

- `Idempotency-Key` (string): Optional. A unique key for this request. Retrying a POST with the same key and body never creates a duplicate job; the same key with a different body returns `409 conflict`.

**Body** (application/json)

- `base` (string, required): The Schema model to move to, a newer base from `GET /v2/models`.

**Returns**

`202` with the job. On completion the endpoint object carries the new base, the new report, `previous`, and `delta`.

**Example request (cURL)**

```bash
curl -X POST 'https://api.schemalabs.ai/v2/endpoints/{endpoint_id}/upgrade' \
  -H "Authorization: Bearer $SCHEMA_API_KEY" \
  -H "Idempotency-Key: <unique key>" \
  -H "Content-Type: application/json" \
  -d '{ "base": "schema-2" }'
```

**Example response (202)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "job_id": "job_7d3c9b2e"
}
```

**Completed (from GET /v2/endpoints/:id) (200)**

```json
{
  "endpoint": "churn",
  "id": "{endpoint_id}",
  "base": "schema-2",
  "held_out": {
    "score": 0.961
  },
  "previous": {
    "base": "schema-1",
    "held_out": 0.948
  },
  "delta": "+0.013",
  "report": "churn.s2.upgrade.2026-08-14",
  "report_id": "r_a7d0"
}
```

> Metering: one fresh pass over the pinned data at the fresh rate.

### Endpoint logs

`GET /v2/endpoints/:id/logs` (scope: `read`)

The endpoint’s operation log: create, pin, refresh, upgrade, and job events. Never row data.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Query parameters**

- `limit` (integer, default 20): Page size.
- `cursor` (string): Cursor from a previous page.

**Returns**

The endpoint’s log entries (`at`, `op`, `detail`) and `total`.

**Example request (cURL)**

```bash
curl 'https://api.schemalabs.ai/v2/endpoints/{endpoint_id}/logs' \
  -H "Authorization: Bearer $SCHEMA_API_KEY"
```

**Example response (200)**

```json
{
  "logs": [
    {
      "at": "2026-08-14T11:20:05Z",
      "op": "upgrade",
      "detail": "schema-1 -> schema-2; report r_a7d0"
    },
    {
      "at": "2026-08-12T09:31:44Z",
      "op": "refresh",
      "detail": "dsv_2b81 -> dsv_91f2 (+310 / ~12 / -4 rows); report r_b5e1"
    },
    {
      "at": "2026-03-10T14:09:31Z",
      "op": "evaluate",
      "detail": "held-out evaluated over 403,200 cells across 2 tables; endpoint live"
    },
    {
      "at": "2026-03-10T14:07:02Z",
      "op": "pin",
      "detail": "ds_crm01 @ dsv_2b81, ds_bill01 @ dsv_c4e7"
    },
    {
      "at": "2026-03-10T14:07:02Z",
      "op": "create",
      "detail": "endpoint created on schema-1, 2 tables pinned (dsv_2b81, dsv_c4e7)"
    }
  ],
  "total": 5
}
```

### Endpoint OpenAPI description

`GET /v2/openapi/:id` (scope: `read`)

Returns the OpenAPI 3.1 document for one live endpoint: its serve operation, the request schema derived from the pinned snapshot (table ids and columns), and the bundle response schema. Import it into any tool that reads OpenAPI.

Agent frameworks, function-calling wrappers, and no-code HTTP nodes read this document to call the endpoint with the right table ids and columns. See [Agents and LLMs](https://docs.schemalabs.ai/integrations/agents).

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Returns**

The OpenAPI document as `application/json`.

**Example request (cURL)**

```bash
curl 'https://api.schemalabs.ai/v2/openapi/{endpoint_id}' \
  -H "Authorization: Bearer $SCHEMA_API_KEY"
```

**Example response (200)**

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "churn",
    "version": "2"
  },
  "servers": [
    {
      "url": "https://api.schemalabs.ai"
    }
  ],
  "paths": {
    "/v2/serve/{endpoint_id}": {
      "post": {
        "operationId": "serve",
        "security": [
          {
            "bearerAuth": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  }
}
```

> The document follows the pinned schema: re-import it after a refresh that changes tables or columns.

> Available once the endpoint is `live`.

### Delete an endpoint

`DELETE /v2/endpoints/:id` (scope: `delete`)

Tears the endpoint down immediately. Its reports remain in the org’s audit trail; its datasets remain under Data, unpinned, and are deleted separately.

**Path parameters**

- `id` (string, required): Endpoint ID or name.

**Returns**

The deleted endpoint id and status.

**Example request (cURL)**

```bash
curl -X DELETE 'https://api.schemalabs.ai/v2/endpoints/{endpoint_id}' \
  -H "Authorization: Bearer $SCHEMA_API_KEY"
```

**Example response (200)**

```json
{
  "id": "{endpoint_id}",
  "deleted": true
}
```

> Deletion is immediate. Callers of the URL receive `404 not_found` from this moment.
