---
title: Endpoints
url: https://docs.schemalabs.ai/endpoints
description: Create an endpoint over pinned data, serve it with a serve-scoped key, refresh when the source changes, and upgrade when a new base ships.
---

# Endpoints

> Create an endpoint over pinned data, serve it with a serve-scoped key, refresh when the source changes, and upgrade when a new base ships.

An endpoint is the persistent, per-customer product object: `{ base, pinned dataset snapshots, configuration, system prompts, held-out report }` at a stable URL. Compose in the Workspace or over the API, publish to a URL, hand the URL to software. Unlike a [quick run](https://docs.schemalabs.ai/api-reference/run), an endpoint has state, and every call runs against that state.

```text
 POST /v2/endpoints { name, data, target, task, base }  -> 202 (job: pin + evaluate + expose)
        │
 ┌──────▼──────────────────────────────────────────────────────────────┐
 │ ENDPOINT churn   base schema-2 · dsv_91f2 · held-out 96.1% (r_a7d0) │
 │ url: /v2/serve/{endpoint_id}   yours, private, live                 │
 │ all outputs: sector · column profile · cross-table map ·            │
 │   missing-value imputation · predictions · held-out                 │
 └──────┬──────────────────────────────────────────────────────────────┘
        ├── serve:   POST /v2/serve/:id            data in → full bundle
        ├── evolve:  POST /v2/endpoints/:id/refresh   re-sync data, re-score
        │            POST /v2/endpoints/:id/upgrade   newer base, re-score
        └── operate: /v2/jobs · /v2/reports · /v2/usage · /v2/keys
```

An endpoint is typically live within minutes.

## Identity and state

| Thing | Value | Mutable |
|---|---|---|
| Endpoint ID | Opaque unique ID, assigned at creation | Never |
| Name | Lowercase slug, for example `churn` | Yes, a rename |
| URL | `https://api.schemalabs.ai/v2/serve/{endpoint_id}` | Never |

Routes accept the id or the name; only the id survives a rename. An endpoint's state is `(base generation, data snapshot, configuration revision)`; each component versions independently, every change is additive, and the endpoint keeps serving throughout:

| Operation | Changes | Mints a report |
|---|---|---|
| `create` | All three, initial | Yes |
| `refresh` | Data snapshot (`dsv_`), fresh split | Yes |
| `upgrade` | Base | Yes |
| Configuration edit (target, task, thresholds, system prompts) | Config revision | No; a target change updates the endpoint's held-out block |

The report trail is the endpoint's history. Configuration edits and renames are made on the endpoint page in the platform; everything else is available on both surfaces.

## Create

[Register the data](https://docs.schemalabs.ai/data#register-a-connection) first, or pass `tables` inline (they are ingested as an upload dataset). `base` defaults to the latest Schema model.

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

# 202 { "endpoint": "churn", "id": "{endpoint_id}",
#        "status": "creating", "job_id": "job_9a5b…" }
```

The id is final from this moment and the endpoint is listed immediately as `creating`. Poll the job or the endpoint until `status` is `live` (see [Async jobs](https://docs.schemalabs.ai/api-reference#async-jobs)), then read the report at `/v2/reports/:id`: check the split, the baseline, and the weakest slice before you show the number to anyone. A new endpoint starts with one default system-prompt fragment. Shapes: [Create an endpoint](https://docs.schemalabs.ai/api-reference/endpoints#create-endpoint).

## Serve

Serving is REST-only and needs a `serve`-scoped key. Give production a key with `serve` alone, so a leaked pipeline key cannot create, change, or delete anything.

```bash
curl -X POST https://api.schemalabs.ai/v2/keys \
  -H "Authorization: Bearer $SCHEMA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "churn-pipeline", "scopes": ["serve"] }'

curl -X POST https://api.schemalabs.ai/v2/serve/{endpoint_id} \
  -H "Authorization: Bearer $SCHEMA_SERVE_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]] } ] }'
```

A serve is live inference over the endpoint's state: the request rows plus the pinned data, returning the full bundle scored by the endpoint. Read the current held-out report and system prompts from the endpoint object (`GET /v2/endpoints/:id`, `read` scope). Request data is validated against the pinned snapshot's schema; a mismatch is a `400 validation_error` naming the divergence. To wrap the endpoint as a tool or narrate the bundle with your own LLM, see [Agents and LLMs](https://docs.schemalabs.ai/integrations/agents).

A serve is distinct from **reading** the endpoint: `GET /v2/endpoints/:id` returns the stored object and the bundle already computed over the pinned data, with no inference.

> **Note: Availability**
Pro and Enterprise carry an availability SLA (terms in the [Terms of Service](https://www.schemalabs.ai/terms)). Per-endpoint and per-key rate limits (`429` with `Retry-After`) and a concurrency cap apply, alongside the org's rate limits.

## Refresh: the data changed

Sync the dataset first, or let refresh do it inline:

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

`202` with a job; on completion the endpoint carries the new snapshot, a `diff { rows_added, rows_changed, rows_removed, rows_unchanged }`, and the new report, scored on a fresh split. Refresh is the only operation that makes an endpoint stop reflecting deleted rows. Schedule it from your data stack when the source updates (see [Workflows and data stack](https://docs.schemalabs.ai/integrations/data-stack)).

## Upgrade: a new base shipped

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

# completed: { "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" }
```

The same pinned data re-runs on the newer base and is re-scored. The endpoint serves the new base from completion; the trail keeps the prior report and the delta reads on the Reports page.

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

## System prompts

An endpoint owns a library of named system-prompt fragments (`sp_...`, each with `name`, `body`, `active`, `order`, `version`). Fragments steer an LLM. Read them from the endpoint object (`GET /v2/endpoints/:id`, the `system_prompts` array) and apply them in your own LLM; Chat applies them on the platform. Authored on the endpoint page (**System Instructions**); each edit is a config revision, applies immediately, and is audit-logged. Chat behavior: [Platform](https://docs.schemalabs.ai/platform#chat).

## Logs, delete, metering

`GET /v2/endpoints/:id/logs` returns the endpoint's operation log (create, refresh, upgrade, job events), never row data. `DELETE /v2/endpoints/:id` tears the endpoint down immediately; its reports remain in the org's audit trail and its datasets remain under Data, unpinned. What each operation costs is in [Usage and billing](https://docs.schemalabs.ai/billing#what-each-operation-costs).
