# SchemaLabs Docs, full text # Quickstart Source: https://docs.schemalabs.ai/quickstart > Get an API key, quick-run Schema across sources that share no key, and publish the same configuration as an endpoint, in about five minutes. ## Get an API key Sign in to the [platform](https://platform.schemalabs.ai) and open **API** in the left nav (available from Plus; see [Plans](https://docs.schemalabs.ai/billing#plans)). Create a key with the `read`, `run`, `manage`, and `serve` scopes: one key covers this walkthrough. Production systems call a live endpoint with a separate key scoped to `serve` only (see [Authentication](https://docs.schemalabs.ai/authentication)). The secret (`sk_live_...`) is shown once; store it in your secrets manager. Export it for the shell you will use: ```bash export SCHEMA_API_KEY="sk_live_..." ``` ## Quick run across sources Schema takes any number of tables from any number of sources in one call; two are enough to show the cross-table map. Send tables from two systems that describe the same people but share no identifier. Column names in the second table are opaque on purpose: Schema understands the values. cURL: ```bash curl -X POST https://api.schemalabs.ai/v2/run \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "tables": [ { "id": "crm_contacts", "columns": ["full_name", "phone", "email", "signup_date"], "rows": [ ["Ana Kaya", "555-0142", "ana.k@example.com", "2024-11-03"], ["Devraj Nair", "555-0199", "d.nair@example.com", "2024-12-01"], ["Mira Ostrom", "555-0210", null, "2025-01-17"] ] }, { "id": "cards_db", "columns": ["c0", "c1", "c2", "c3"], "rows": [ ["KAYA, ANA", "+1 555 0142", "Visa", 12000], ["NAIR, DEVRAJ", "+1 555 0199", "Mastercard", 6500], ["OSTROM, MIRA", "+1 555 0210", "Amex", null] ] } ], "target": { "mode": "auto" } }' ``` Python: ```python import os, requests tables = [ {"id": "crm_contacts", "columns": ["full_name", "phone", "email", "signup_date"], "rows": [["Ana Kaya", "555-0142", "ana.k@example.com", "2024-11-03"], ["Devraj Nair", "555-0199", "d.nair@example.com", "2024-12-01"], ["Mira Ostrom", "555-0210", None, "2025-01-17"]]}, {"id": "cards_db", "columns": ["c0", "c1", "c2", "c3"], "rows": [["KAYA, ANA", "+1 555 0142", "Visa", 12000], ["NAIR, DEVRAJ", "+1 555 0199", "Mastercard", 6500], ["OSTROM, MIRA", "+1 555 0210", "Amex", None]]}, ] r = requests.post( "https://api.schemalabs.ai/v2/run", headers={"Authorization": f"Bearer {os.environ['SCHEMA_API_KEY']}"}, json={"tables": tables, "target": {"mode": "auto"}}, ) r.raise_for_status() bundle = r.json() ``` JavaScript: ```javascript const tables = [ { id: 'crm_contacts', columns: ['full_name', 'phone', 'email', 'signup_date'], rows: [['Ana Kaya', '555-0142', 'ana.k@example.com', '2024-11-03'], ['Devraj Nair', '555-0199', 'd.nair@example.com', '2024-12-01'], ['Mira Ostrom', '555-0210', null, '2025-01-17']] }, { id: 'cards_db', columns: ['c0', 'c1', 'c2', 'c3'], rows: [['KAYA, ANA', '+1 555 0142', 'Visa', 12000], ['NAIR, DEVRAJ', '+1 555 0199', 'Mastercard', 6500], ['OSTROM, MIRA', '+1 555 0210', 'Amex', null]] }, ]; const res = await fetch('https://api.schemalabs.ai/v2/run', { method: 'POST', headers: { Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ tables, target: { mode: 'auto' } }), }); const bundle = await res.json(); ``` Headers can be real names or opaque labels; Schema does not need them. ## Read the bundle The response is the full bundle. The parts worth reading first: ```json { "summary": { "tables": 2, "records_linked": 3, "keys_used": "none", "prediction": { "target": "cards_db.c2", "task": "classification", "tier": "in_context", "held_out": null } }, "tables": [ { "id": "crm_contacts", "sector": { "top1": { "name": "consumer financial services", "confidence": 0.89 } }, "column_profile": [ { "name": "phone", "role": "phone", "role_confidence": 0.97, "pii": true }, "..." ] } ], "cross_table_map": { "column_alignment": [ { "a": "crm_contacts.phone", "b": "cards_db.c1", "attribute": "phone", "confidence": 0.96, "example": { "a": "555-0142", "b": "+1 555 0142" } }, { "a": "crm_contacts.full_name", "b": "cards_db.c0", "attribute": "name", "confidence": 0.90 } ], "entity_matches": [ { "a_row": 0, "b_row": 0, "confidence": 0.94, "matched_on": ["phone", "name"] }, "..." ] }, "imputation": { "filled": [ { "table": "cards_db", "row": 2, "column": "c3", "value": 8400.0, "method": "schema-2" } ], "row_confidence": [ { "table": "cards_db", "row": 2, "confidence": 0.81 } ] }, "target_selection": { "mode": "auto", "column": "cards_db.c2", "reason": "most predictable among eligible categorical columns" }, "prediction": { "task_type": "classification", "classes": ["Amex", "Mastercard", "Visa"], "results": [ { "row": 0, "label": "Visa", "confidence": 0.94 } ], "note": "in-context prediction; create an endpoint for a held-out score." } } ``` `keys_used` is `"none"`: the phone columns were aligned from their values despite different formats, and the names matched despite different order. The missing value in `cards_db.c3` (a credit limit, though the column is unnamed) was filled with a row-level confidence; imputation fills numeric and categorical cells; a missing email stays missing because identifying values (email, phone, name) are never invented. The prediction is in-context and labeled `held_out: null`, which is what the `note` points at: publish the same configuration as an endpoint for a held-out score. Every field is described in [Outputs](https://docs.schemalabs.ai/outputs). ## Publish it as an endpoint The same configuration, pinned and scored. Pass the same two tables inline (they are ingested as an upload dataset under Data and pinned), or reference registered datasets and connections in `data` instead: cURL: ```bash curl -X POST https://api.schemalabs.ai/v2/endpoints \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: " \ -d '{ "name": "card-brand", "tables": [ { "id": "crm_contacts", "columns": ["full_name", "phone", "email", "signup_date"], "rows": [["Ana Kaya", "555-0142", "ana.k@example.com", "2024-11-03"], ["Devraj Nair", "555-0199", "d.nair@example.com", "2024-12-01"], ["Mira Ostrom", "555-0210", null, "2025-01-17"]] }, { "id": "cards_db", "columns": ["c0", "c1", "c2", "c3"], "rows": [["KAYA, ANA", "+1 555 0142", "Visa", 12000], ["NAIR, DEVRAJ", "+1 555 0199", "Mastercard", 6500], ["OSTROM, MIRA", "+1 555 0210", "Amex", null]] } ], "target": { "mode": "auto" }, "task": { "mode": "auto" } }' ``` Python: ```python r = requests.post( "https://api.schemalabs.ai/v2/endpoints", headers={"Authorization": f"Bearer {os.environ['SCHEMA_API_KEY']}", "Idempotency-Key": ""}, json={"name": "card-brand", "tables": tables, "target": {"mode": "auto"}, "task": {"mode": "auto"}}, ) job = r.json() # 202: { "endpoint": "card-brand", "id": "...", "status": "creating", "job_id": "job_..." } ``` JavaScript: ```javascript const res = await fetch('https://api.schemalabs.ai/v2/endpoints', { method: 'POST', headers: { Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': '', }, body: JSON.stringify({ name: 'card-brand', tables, target: { mode: 'auto' }, task: { mode: 'auto' } }), }); const job = await res.json(); // 202 ``` The response is `202` with the endpoint id and a `job_id`. `base` was omitted, so the endpoint is created on the latest Schema model (name one to fix the base). Creation pins the data, evaluates held-out, and exposes the URL, typically in minutes. Poll `GET /v2/jobs/{job_id}` until `status` is `done`, or watch the endpoint flip from `creating` to `live` on the platform. ## Serve it Once live, POST new rows to the endpoint URL (the key needs the `serve` scope) and get the bundle back, scored by the endpoint over its pinned data. The held-out report lives on the endpoint object and at `/v2/reports/{id}`: ```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": ["full_name","phone","email","signup_date"], "rows": [["Yuki Sato","555-0301", "y.sato@example.com","2025-08-01"]] } ] }' ``` ## Next - [Endpoints](https://docs.schemalabs.ai/endpoints): State, refresh, upgrade, and what the URL guarantees. - [Held-out reports](https://docs.schemalabs.ai/held-out): How the score is computed and what the report contains. - [Usage and billing](https://docs.schemalabs.ai/billing): Exactly what each of these calls drew. --- # Introduction Source: https://docs.schemalabs.ai/introduction > What Schema is, what one call returns, and the two ways to consume it. Schema is a Data Language Model (DLM): a foundation model that understands tabular data natively. Send tables and databases as they are, across any number of sources; one call returns what the data is, how the sources relate, what is missing, and what can be predicted from it. No shared keys, no labels, no preprocessing. ```text tables in ─────────► Schema ─────────► the bundle any number of sources sector · column profile · cross-table map no shared keys missing-value imputation · predictions no labels · no preprocessing every output carries its confidence ``` ## What one call returns Every pass, whether a quick run, an endpoint creation, or a call to a live endpoint, emits this bundle (the held-out report lives on the endpoint object): | Output | What it is | |---|---| | **Sector** | The industry sector of each table, from cell values alone. Top-1 and top-5, each with a confidence. | | **Column profile** | Per column: role, type, missingness, PII flag, with a confidence. | | **Cross-table map** | Which columns and rows across 2+ tables are the same attribute or entity, with no shared keys. | | **Missing-value imputation** | Filled numeric and categorical cells, with a row-level confidence. | | **Predictions** | Classification, regression, or anomaly on a target column, with a per-row confidence. | | **Held-out report** | Accuracy on rows the model never saw. Endpoints only. | The vocabulary is identical across the API and the platform. Field-by-field shapes: [Outputs](https://docs.schemalabs.ai/outputs). ## Two modes A quick run (`POST /v2/run`) is stateless: one pass, the bundle back, nothing stored. An endpoint (`POST /v2/endpoints`, then `POST /v2/serve/:id`) pins data, carries a held-out report, and serves at a stable URL. See [Endpoints](https://docs.schemalabs.ai/endpoints). ## Surfaces - **REST API** at `https://api.schemalabs.ai/v2/`: plain JSON over HTTPS, idempotency keys, job polling, and a per-endpoint OpenAPI description for every live endpoint. Any HTTP client works. [API reference](https://docs.schemalabs.ai/api-reference). - **Platform** at [platform.schemalabs.ai](https://platform.schemalabs.ai): Dashboard, Data, Quick run, Workspace, Endpoints, Reports, Chat, API. A client of the same API; both surfaces draw one usage meter. [Platform](https://docs.schemalabs.ai/platform). - **Base models**: runs and new endpoints default to the latest Schema model; every model, its status, and its card live in the [model registry](https://www.schemalabs.ai/model-card). [Models](https://docs.schemalabs.ai/models). - [Quickstart](https://docs.schemalabs.ai/quickstart): Key, first run, first endpoint. - [Outputs](https://docs.schemalabs.ai/outputs): Every field of the response. - [Usage and billing](https://docs.schemalabs.ai/billing): What a call costs. --- # Authentication Source: https://docs.schemalabs.ai/authentication > API keys, scopes, and how the platform and the API share one identity and one meter. Every API request carries an org-scoped API key as a bearer token: ```http Authorization: Bearer ``` Keys are created on the platform's **API** page or with [`POST /v2/keys`](https://docs.schemalabs.ai/api-reference/keys#create-key). The secret is shown once, at creation, and never again; listings show the key id (`key_...`) and a preview of the secret. Store secrets in a secrets manager and rotate with [`POST /v2/keys/:id/rotate`](https://docs.schemalabs.ai/api-reference/keys#rotate-key). ## Two modes, one org | Mode | Who | How | Bills to | |---|---|---|---| | Interactive | Humans | Web sign-in on [platform.schemalabs.ai](https://platform.schemalabs.ai) | the org | | Headless | CI, agents, pipelines, apps | `Authorization: Bearer $SCHEMA_API_KEY` | the same org | An endpoint created over the API appears in the platform Endpoints list immediately. Usage from both surfaces draws one org meter and one bill; the Usage page and [`GET /v2/usage`](https://docs.schemalabs.ai/api-reference/usage) render the same numbers, broken down by surface. ## Scopes Outputs are bundled, so keys are never scoped per output. They are scoped by operation: | Scope | Allows | |---|---| | `read` | List and get endpoints, data, reports, jobs, usage, models, keys | | `run` | Stateless inference on the base models: `POST /v2/run` | | `serve` | Call live endpoints: `POST /v2/serve/:id` | | `manage` | Create endpoints, refresh, upgrade, connect and sync data, pin and unpin, generate synthetic data, cancel jobs, manage keys | | `delete` | Delete endpoints and data, revoke keys | A key can hold any combination. Give production systems the least they need: a pipeline that only serves endpoints needs `serve` alone. ## Errors | Situation | Response | |---|---| | Missing or malformed key | `401` `auth_error` | | Key revoked | `401` `auth_error` | | Key lacks the route's scope | `403` `auth_error` | See [Errors](https://docs.schemalabs.ai/api-reference#errors) for the envelope. ## What a key covers A key buys inference: `run` returns the output bundle, and an endpoint serves your configuration over the shared base (or an Enterprise fine-tune) at a URL. Connection credentials you register are encrypted at rest and write-only. --- # Models Source: https://docs.schemalabs.ai/models > The Schema models you can run on and create endpoints on, how to choose a base, and how an upgrade moves an endpoint to a newer base. Data and Endpoints are the objects you create; every self-serve operation is inference. "Model" always means a Schema base. The [model registry](https://www.schemalabs.ai/model-card) is the documentation of record for every Schema model, current and retired: intended use, training data, evaluation, lifecycle status, and known limitations. `GET /v2/models` returns the bases available to your org and which one is the default, plus any Enterprise fine-tune you own; lifecycle, evaluations, and task support live on each model card in the registry. ## Choosing a base Use the latest Schema model: it is what runs and new endpoints use when `base` is omitted, and it supports all three prediction task types (classification, regression, anomaly). Name a specific base only when you need a fixed comparison point. A retired model answers runs, creations, and serves with `422 capability_unavailable`. An endpoint created on it keeps that base until you move it with `POST /v2/endpoints/:id/upgrade` (see [Endpoints](https://docs.schemalabs.ai/endpoints#upgrade-a-new-base-shipped)); its earlier reports keep their base name, so the trail stays readable. Bases are named `schema-{n}` (`s{n}` in report names); the API version `/v2/` is a separate axis. See [Identifiers](https://docs.schemalabs.ai/api-reference#identifiers). ## Enterprise fine-tunes Fine-tuning is an Enterprise program. A fine-tune is served behind an ordinary endpoint you own, bills as ordinary inference, and carries the same held-out report contract. Contact [sales](mailto:info@schemalabs.ai). --- # Data Source: https://docs.schemalabs.ai/data > Connect sources, sync snapshots, pin datasets for the cached rate, generate synthetic data, run batch jobs, and see what is stored, deleted, and audited. Data is one group covering connections and datasets, at `/v2/data` and on the platform's Data page. A **connection** is where data comes from. A **dataset** is an ingested or registered table. Each ingest or sync mints an immutable **snapshot** (`dsv_...`), which is what endpoints pin and held-out reports stamp. ## Sources | Family | Sources | Ref shape | |---|---|---| | Upload | CSV, Excel, JSON | `upload` (platform), or inline `tables` in a request | | Databases | PostgreSQL, MySQL, Supabase, MongoDB, Databricks, Snowflake, Pinecone, Chroma | `postgres://...`, `snowflake://...`, ... | | Cloud storage | Google Drive, GCP Storage, AWS S3 | `gdrive://...`, `gs://...`, `s3://...` | | API | REST, GraphQL | `rest+https://...`, `graphql+https://...` | Small files upload directly on the platform or travel inline as `tables` in a request. Everything else is a connection: registered once, referenced by id or scheme ref. ## Register a connection cURL: ```bash curl -X POST https://api.schemalabs.ai/v2/data/connect \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "source": "postgres://db.example.com:5432/crm/public/contacts", "name": "crm_contacts", "credentials": { "user": "schema_reader", "password": "…" }, "options": { "query": "select * from contacts where deleted_at is null" } }' ``` Python: ```python import os, requests SCHEMA_API_KEY = os.environ["SCHEMA_API_KEY"] r = requests.post("https://api.schemalabs.ai/v2/data/connect", headers={"Authorization": f"Bearer {SCHEMA_API_KEY}"}, json={"source": "s3://example-exports/crm/2026/", "name": "crm_exports", "credentials": {"access_key_id": "…", "secret_access_key": "…"}}) ``` JavaScript: ```javascript const SCHEMA_API_KEY = process.env.SCHEMA_API_KEY; const res = await fetch('https://api.schemalabs.ai/v2/data/connect', { method: 'POST', headers: { Authorization: `Bearer ${SCHEMA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ source: 'rest+https://api.example.com/v1/customers', name: 'customers_api', credentials: { bearer: '…' } }), }); ``` The response returns the connection (`conn_...`) and the dataset it produced (`ds_...`) with its first snapshot (`dsv_...`). Reference it anywhere data is accepted: `"data": ["ds_crm01", "snowflake://sales/live"]`. A quick run uses it transiently and registers nothing new; endpoint creation pins it. `GET /v2/data/:id` returns the catalog profile: rows, columns, current snapshot, pin state, and which endpoints pin it. Full family: [Data API](https://docs.schemalabs.ai/api-reference/data). ## Snapshots and sync Every ingest and every `POST /v2/data/:id/sync` mints a new immutable `dsv_...`. Endpoints and reports reference the exact snapshot they ran on; an endpoint keeps serving its current snapshot until you `refresh` it, even after a sync has minted a newer one. Schedule syncs from your orchestrator, then refresh dependent endpoints (see [Workflows and data stack](https://docs.schemalabs.ai/integrations/data-stack)). ## Pinning Pinning is a state on a registered dataset, set with `POST /v2/data/:id/pin` and `/unpin`. Pin state controls the cached rate even when no endpoint exists. | State | Repeat calls over the dataset | Storage | Expiry | |---|---|---|---| | Pinned | Cached rate, indefinitely | Counts toward plan storage | Never | | Unpinned | Cached rate briefly after last use, then fresh | Not counted | Short | After a sync, the next pass bills added and changed rows fresh, unchanged rows cached, and removed rows nothing. An endpoint keeps serving its pinned snapshot until you refresh it. Endpoint creation pins its datasets automatically. A dataset pinned by a live endpoint cannot be unpinned or deleted while that endpoint serves; the `409` names the dependent endpoint. > **Tip: Agents re-querying the same table** Pin once, then every subsequent quick run that references the dataset bills unchanged rows at the cached rate. ## Batch and large runs Batch is a service tier, elected per job with `"options": { "processing": "batch" }`. The job bills the batch rate and runs with a completion window, shown on the job's eta. It applies to inference and synthetic generation. ```http POST /v2/run { "data": ["snowflake://example/warehouse/events_2025"], "target": { "column": "converted" }, "options": { "processing": "batch", "out": "warehouse://schema.outputs" } } ``` `202` with a job. The bundle is written to `out` (a file or warehouse table) on completion, or retrievable from the job when `out` is omitted and the result fits. A job that misses its window expires unbilled and you resubmit. Batch concurrency per plan: [Rate limits](https://docs.schemalabs.ai/billing#rate-limits). ## Synthetic data `POST /v2/data/generate { "sector": "hospital operations", "rows": 5000, "name": "hospital_ops_synth" }` produces a synthetic dataset for a sector with realistic per-column ranges, as an ordinary dataset you can run on, pin, or create an endpoint on. Useful when representative data is thin or as a safe test fixture. The response is the new dataset; large generations run as batch jobs. `sector` uses the same vocabulary the sector output returns; if unsure of the exact name, quick-run a small real table and use its `sector.top1.name`. Generation is the one operation where the output is the meter (`C = cells generated`). ## Persistence follows the operation | Operation | Stored | |---|---| | `run`, endpoint calls | No dataset is registered; submitted tables are used for the single pass and discarded. The run's job and run record remain visible in Jobs and Reports. Endpoint logs keep operation events, never row data. | | Endpoint create, refresh, upgrade | The pinned snapshots, the held-out report with its split seed and partition, the configuration and system prompts. Inline `tables` at creation become an upload-type dataset under Data. | | Data connect, sync | The dataset snapshot; connection credentials encrypted at rest, write-only. | Everything stored is visible under Data, deletable immediately, and counted toward storage. Residency and deployment options: [Trust Center](https://www.schemalabs.ai/trust). ## Deletion | Resource | Policy | |---|---| | Datasets and connections | `DELETE /v2/data/:id` is immediate and irreversible. A dataset pinned by a live endpoint is refused with `409 conflict` naming the endpoint: delete the endpoint or refresh it onto other data first. | | Endpoints | Immediate on `DELETE`. Reports remain in the audit trail; its datasets remain under Data and are unpinned. | | Reports | Retained for the life of the org. | | API keys | Revoked immediately with `DELETE /v2/keys/:id`; the key stops working and leaves the listing. | **To remove data and its influence entirely**: remove the rows at source (or delete the dataset), `sync`, then `refresh` dependent endpoints. Purged rows leave the stored snapshot and the pinned context in one step. Data handling terms are in the [DPA](https://www.schemalabs.ai/dpa). ## Credentials and audit Connection credentials are encrypted at rest and are write-only; rotate by re-entering. The audit log records connection, sync, key, and deletion events with actor and timestamp. ## Related [Trust Center](https://www.schemalabs.ai/trust) · [Privacy](https://www.schemalabs.ai/privacy) · [DPA](https://www.schemalabs.ai/dpa) · [Sub-processors](https://www.schemalabs.ai/sub-processors) · [Security](https://www.schemalabs.ai/security) --- # Outputs Source: https://docs.schemalabs.ai/outputs > The one response every pass returns: sector, column profile, cross-table map, imputation, predictions, and on endpoints the held-out block. Every pass emits the full bundle: producing all outputs is one pass, so you always get all of them. A quick run returns the bundle for the tables you sent; an endpoint object carries the bundle computed over its pinned data; a serve returns it for the request data, scored by the endpoint; the held-out block is on the endpoint object. Field-by-field shapes are in the [API reference](https://docs.schemalabs.ai/api-reference/run). > **Note: Atomic** A pass either returns the full bundle or fails as `job_failed`. Slices scoped by contract (the cross-table map on one table, the prediction slice under `target.mode: "none"`) are documented `null`, never absent by failure. Failed jobs bill nothing. ## Sector Vertical-agnostic sector identification: the industry sector of each table, from cell values alone, any domain, no metadata required. `top1 { name, confidence }` and `top5[]`, each entry with its own confidence. Blind to column names: it holds with headers stripped or coded. Per-model results on blind sector identification are in the [model registry](https://www.schemalabs.ai/model-card). Use it to catalog a data lake, route incoming files, or triage a dataset before anyone opens it. ## Column profile Per column: `name`, `role`, `role_confidence`, `type`, `missing_pct`, `pii`. | Field | Values | |---|---| | `role` | `email`, `phone`, `name`, `date`, `address`, `code`, `identifier`, `measure`, `category`, `text`, `boolean`, `currency`, `url`, `geo` | | `type` | `categorical`, `numeric`, `datetime`, `text`, `boolean` | | `missing_pct` | Fraction of missing cells, 0 to 1 | | `pii` | `true` when the role is personally identifying (name, email, phone, address, national id, ...) | Use `role` and `pii` to route, mask, or gate columns in your own systems. ## Cross-table map Across any number of tables from any number of sources (two or more), the bundle relates the sources with no shared key, no lookup table, and no name matching: - `column_alignment[]`: column pairs that carry the same attribute, each with `attribute`, `confidence`, and a verbatim `example` from the cells (`555-0142` and `+1 555 0142` under one `phone`). - `entity_matches[]`: row pairs that are the same record (surfaced as **entity matches**), with `confidence` and `matched_on`, the aligned attributes that agreed for that pair. - `unified_schema { shared, a_only, b_only }`: the proposed fold. - `unified_rows`: the map materialized into joined records from the original cell values, one per matched entity, with `provenance` per column and `shared_variants` showing each source's raw value. When shared variants disagree beyond formatting, that flags a possible mis-alignment rather than silently picking one side. `total` is the full count; `rows` is a sample, paginated by cursor. Alignments and matches are reported above your `options.confidence_threshold`. Every alignment and every match ships its confidence and its evidence (`example`, `matched_on`); the map is a proposal for your review, never auto-applied: act on high-confidence matches automatically and queue the rest at thresholds you choose. On an endpoint, pin two or more datasets and the endpoint's bundle carries the map over the pinned data; a serve scores the request tables against the pinned context, and the held-out split is by matched entity. Per-model results for keyless entity matching and name-blind schema matching are in the [model registry](https://www.schemalabs.ai/model-card). ## Missing-value imputation `filled[]` lists each reconstructed cell (`table`, `row`, `column`, `value`, `method`); confidence is **row-level, not per cell**, in `row_confidence[]` (`table`, `row`, `confidence`). Numeric and categorical gaps are both filled; a categorical fill is a class label, not the most common value. Identifying columns (`pii: true`) are never filled. Missing cells are metered like any other cell. ## Predictions Three task types, all on the current base, all in-context: predictions use the labeled rows present (the request's, or the endpoint's pinned data). Nothing trains. | Task | Per row | |---|---| | Classification | `label`, `confidence` (max class probability), full `probabilities` vector | | Regression | `value`, `median`, `std`, `quantiles` | | Anomaly | `anomaly_score`, `is_anomaly`, and the `threshold` used | ```json { "prediction": { "target": "cards_db.card_brand", "task_type": "classification", "tier": "in_context", "classes": ["Amex", "Mastercard", "Visa"], "dropped_rows": { "missing_target": 0 }, "results": [ { "row": 0, "label": "Visa", "confidence": 0.94, "probabilities": { "Amex": 0.01, "Mastercard": 0.05, "Visa": 0.94 } } ] } } ``` ```json { "prediction": { "target": "credit_limit", "task_type": "regression", "tier": "in_context", "results": [ { "row": 0, "value": 12480.0, "median": 12010.0, "std": 3120.0, "quantiles": { "0.1": 8400.0, "0.5": 12010.0, "0.9": 17900.0 } } ] } } ``` ```json { "prediction": { "task_type": "anomaly", "threshold": 0.90, "results": [ { "row": 0, "anomaly_score": 0.97, "is_anomaly": true }, { "row": 1, "anomaly_score": 0.12, "is_anomaly": false } ] } } ``` Use regression quantiles where you need the range and not a single number (pricing, claim severity, reserve setting). Anomaly runs unsupervised with no target, on explicit request only: fraud screening, equipment monitoring, data-quality checks with no labeled history. **Missing targets.** Rows whose target is missing are counted in `dropped_rows.missing_target`; a missing value never enters `classes` and is never a predictable label. **Two tiers.** A quick run prediction carries `held_out: null`; an endpoint's current held-out score is on the endpoint object and in the report. ## Target and task selection Both are first-class on quick runs and on endpoint creation, with the same shapes; both default to `auto` and both explain themselves in the response. ```jsonc "target": { "mode": "auto" } // smart-target: Schema picks and explains "target": { "column": "churn" } // you pick; "table.column" on multi-table input "target": { "mode": "none" } // understanding-only: no prediction slice "task": { "mode": "auto" } // categorical -> classification, numeric -> regression "task": { "type": "anomaly" } // explicit only; needs no target ``` The response carries `target_selection { mode, column, reason, overridable, candidates[] }`, where `candidates` lists every column with whether it was selected or ruled out and a plain-language reason, and `task_selection { mode, type, reason, overridable }`. You see the decision and the rationale. On an endpoint you can override the target afterwards from the endpoint page in the platform; a target change is a configuration revision and triggers a re-score. Smart-target picks categorical targets; to predict a numeric column, name it in `target.column` and set `task.type` to `regression`. On a text-only table, name the target in `target.column`; the platform prompts for it. `target.mode: "none"` is the understanding-only configuration: the bundle ships sector, column profile, cross-table map, and imputation, and `prediction` is `null`. ## Held-out (endpoints only) ```json { "held_out": { "score": 0.961, "auc": 0.974, "baseline_majority": 0.710, "split": { "method": "in_context_holdout", "train_rows": 17920, "test_rows": 4480, "seed": 7 }, "data_version": "2026-08-12 09:31", "data_snapshot": "dsv_91f2", "weakest_slice": "tenure<3mo: 0.84", "report_url": "/v2/reports/r_a7d0" } } ``` Present on the endpoint object. See [Held-out reports](https://docs.schemalabs.ai/held-out). ## Confidence, everywhere | Output | Confidence is | |---|---| | Sector | Per top-5 entry | | Column profile | Per column, on the role | | Cross-table map | Per column alignment and per entity match, above `confidence_threshold` | | Imputation | Per row (not per cell) | | Predictions | Per row (`confidence` for classification, distribution for regression, `anomaly_score` for anomaly) | Confidences are probabilities; calibration results per model are in the [model card](https://www.schemalabs.ai/model-card/schema-2). ## Large outputs and evolution `unified_rows.rows`, `prediction.results`, and `imputation.filled` return a first page inline (`sample_shown`) plus a cursor; `total` is always the full count. Retrieve the rest with cursor pagination on the endpoint or report resource, or direct a large run's bundle to a file or warehouse table with `options.out`. Adding fields to the bundle is non-breaking and announced; renaming or removing fields happens only at a new API version. Tolerate unknown fields. See [Pagination](https://docs.schemalabs.ai/api-reference#pagination). --- # Endpoints Source: https://docs.schemalabs.ai/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: " \ -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: " ``` `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: " \ -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). --- # Held-out reports Source: https://docs.schemalabs.ai/held-out > How the held-out score is computed by in-context split evaluation, what every report contains, and exactly what the number claims. Every endpoint ships with a held-out score measured on rows whose target values the model never saw when predicting them, stated with its split, seed, baseline, and data snapshot. It is computed at endpoint creation, at every refresh, and at every base upgrade. ## How it is computed The score is measured on rows the model did not see: a stratified test partition is held out (default 80/20), the endpoint predicts those rows, and the predictions are scored against the true values. The report records the method (`in_context_holdout`), the exact partition sizes, and the seed. A refresh scores a fresh split on the renewed snapshot. Every score is stated against a baseline, with the metric set by task type: - Classification: accuracy, ROC-AUC (one-vs-one for multiclass), per-class; baseline is the majority class. - Regression: RMSE, NRMSE, R-squared, plus quantile calibration (coverage of predicted intervals); baseline is the mean or last value. - Anomaly: precision, recall, PR-AUC at the chosen threshold where labels exist; score separation and threshold, flagged unsupervised, where they do not; baseline is the random or expected rate. - Imputation: NRMSE on held-out known cells. ## What the score measures Held-out is a creation-time estimate on unseen rows from the same dataset, tied to a data snapshot. Live production accuracy is a separate measurement, taken from labels that arrive after deployment. The report stamps the snapshot it ran on. ## What every report contains Two layers: a badge everywhere, a report on click. Every number travels with its provenance. ```text +-----------------------------+ | Held-out 96.1% | the badge, inline on every endpoint output | 4,480 test rows | +-----------------------------+ ``` ```text + HELD-OUT REPORT -------------------------------------------------+ | Target: crm_contacts.c3 Data: dsv_91f2 · 2026-08-12 09:31 | | Method: in-context holdout, seed 7 Train 17,920 / Test 4,480 | | | | Held-out accuracy 96.1% (upgrade report r_a7d0, schema-2) | | ROC-AUC 0.974 | | Baseline (majority) 71.0% +25.1 pp lift | | | | Confidence probabilities (see model card) | | Confidence spread most predictions high-confidence | | Per-class: retained 97% · churned 91% | | Weakest slice: tenure<3mo -> 84% | +------------------------------------------------------------------+ ``` Confidences are probabilities; calibration results per model are in the [model card](https://www.schemalabs.ai/model-card/schema-2). For mapping tasks, each aligned pair carries its own confidence in the cross-table map. ## Naming and history Every create, refresh, and upgrade emits exactly one report. Display name: `{endpoint}.{base}.{op}.{YYYY-MM-DD}`, with the base abbreviated (`s1`, `s2`); stable id: `r_...`. ```text churn.s1.create.2026-03-10 r_8f3a pinned (production baseline) churn.s1.refresh.2026-08-12 r_b5e1 fresh split; reflects removals churn.s2.upgrade.2026-08-14 r_a7d0 new base ``` Star any report on the platform's Reports page to pin it: pinned reports float to the top of the endpoint's history. **Compare** takes any two reports side by side, which is where the upgrade delta is read. Reports are kept for the life of the org, listed newest first with pinned reports first; deleting an endpoint does not delete its reports. Reference: [Reports](https://docs.schemalabs.ai/api-reference/reports). --- # Agents and LLMs Source: https://docs.schemalabs.ai/integrations/agents > Give your agent Schema as a tool, over MCP from any framework or over REST with one function-calling definition, and narrate the bundle with your own LLM. An agent that meets raw tables mid-task gets the bundle back: sector, column profile, cross-table map, filled values, and predictions, each with a confidence. It reasons over that structured JSON, not over rows. Two ways to give it the tool. - [Over MCP](https://docs.schemalabs.ai/mcp): One remote server, every platform and API operation as a tool. Any framework that consumes MCP servers connects to the same URL. - [Over REST](#over-rest): One function-calling tool that posts to the API, or an endpoint wrapped from its OpenAPI description. ## The pattern A data agent holds the Schema tool; the orchestrator routes data tasks to it; the bundle flows on to reasoning and action agents. ```text orchestrator ──► data agent ──► Schema (MCP tool, or POST /v2/serve/:id or /v2/run) ▲ │ └── the bundle ◄─┘ sector · profile · map · imputation · predictions ``` The lifecycle: a **quick run** to understand a new source (nothing persists), **create an endpoint** when the same understanding should persist and carry a held-out score (a vertical product typically creates one per customer at onboarding), then **refresh and upgrade** to keep it current. Branch on confidence at every step: act above your threshold, queue the rest for review, and mask columns flagged `pii` before they leave the agent. ## Over MCP Point your framework's MCP client at `https://mcp.schemalabs.ai/mcp` with a dedicated key and the tools appear: `run`, `create`, `serve`, `refresh`, `upgrade`, data, reports, jobs, and usage. Snippets for CrewAI, LangChain and LangGraph, LlamaIndex, Bedrock AgentCore, Snowflake, Databricks, Replit, and n8n are on the [MCP server](https://docs.schemalabs.ai/mcp#agent-frameworks) page, and the tool descriptions tell the model what each one returns and when to call it. ## Over REST Give your LLM one tool. The definition below is what the model needs to call a quick run; for a live endpoint, fix the URL to `/v2/serve/{endpoint_id}` and keep `tables` only. ```json { "name": "schema_run", "description": "Sector, roles, PII, matches, imputation, prediction on raw tables.", "parameters": { "type": "object", "properties": { "tables": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "columns": { "type": "array", "items": { "type": "string" } }, "rows": { "type": "array", "items": { "type": "array" } } }, "required": ["id", "columns", "rows"] } }, "target": { "type": "object", "properties": { "mode": { "enum": ["auto", "none"] }, "column": { "type": "string" } } } }, "required": ["tables"] } } ``` The tool body is a `POST /v2/run` with the arguments as the request body and `Authorization: Bearer $SCHEMA_API_KEY`. Return the bundle, or a trimmed view of it, as the tool result. An endpoint also publishes its own OpenAPI description at `https://api.schemalabs.ai/v2/openapi/{endpoint_id}` ([reference](https://docs.schemalabs.ai/api-reference/endpoints#endpoint-openapi)). LangChain, LlamaIndex, CrewAI, and AutoGen wrap a tool from that spec directly, and observability layers such as LangSmith trace it like any other tool call. ## Narration Let your own LLM explain a result. Pass the bundle with the endpoint's `system_prompts` (active fragments, in order) as the system prompt; read them from the endpoint object, which needs the `read` scope. See [System prompts](https://docs.schemalabs.ai/endpoints#system-prompts). Python: ```python import os, json, requests SCHEMA_API_KEY = os.environ["SCHEMA_API_KEY"] HEADERS = {"Authorization": f"Bearer {SCHEMA_API_KEY}", "Content-Type": "application/json"} EP_ID = "{endpoint_id}" SERVE_URL = f"https://api.schemalabs.ai/v2/serve/{EP_ID}" # llm.chat(): your own model call bundle = requests.post(SERVE_URL, headers=HEADERS, json={"tables": tables}).json() ep_url = f"https://api.schemalabs.ai/v2/endpoints/{EP_ID}" ep = requests.get(ep_url, headers=HEADERS).json() fragments = sorted(ep["system_prompts"], key=lambda f: f["order"]) system = "\n\n".join(f["body"] for f in fragments if f["active"]) user = f"Question: {q}\n\nSchema bundle:\n{json.dumps(bundle)}" answer = llm.chat(system=system, user=user) ``` JavaScript: ```javascript const SCHEMA_API_KEY = process.env.SCHEMA_API_KEY; const HEADERS = { Authorization: `Bearer ${SCHEMA_API_KEY}`, 'Content-Type': 'application/json' }; const EP_ID = '{endpoint_id}'; const SERVE_URL = `https://api.schemalabs.ai/v2/serve/${EP_ID}`; // llm.chat(): your own model call const res = await fetch(SERVE_URL, { method: 'POST', headers: HEADERS, body: JSON.stringify({ tables }) }); const bundle = await res.json(); const epUrl = `https://api.schemalabs.ai/v2/endpoints/${EP_ID}`; const ep = await (await fetch(epUrl, { headers: HEADERS })).json(); const system = ep.system_prompts.filter(f => f.active) .sort((a, b) => a.order - b.order).map(f => f.body).join('\n\n'); const answer = await llm.chat({ system, user: `Question: ${q}\n\nSchema bundle:\n${JSON.stringify(bundle)}` }); ``` Over MCP the client's own model narrates; `get_endpoint` returns the same system-prompt fragments. ## Keys for agents One dedicated, named key per agent. Give it `run` for quick runs, or `serve` only for production, so a leaked key cannot create, change, or delete anything. See [Authentication](https://docs.schemalabs.ai/authentication). Pin datasets an agent re-queries so unchanged rows bill the cached rate ([Pinning](https://docs.schemalabs.ai/data#pinning)), and elect batch for offline sweeps. ## Next - [MCP server](https://docs.schemalabs.ai/mcp): Add the server to your client or framework. - [Endpoints](https://docs.schemalabs.ai/endpoints): State, refresh, upgrade, and what the URL guarantees. - [Outputs](https://docs.schemalabs.ai/outputs): Every field in the bundle, with its confidence. --- # MCP server Source: https://docs.schemalabs.ai/mcp > Use Schema from your AI. One remote server for Claude, ChatGPT, Cursor, VS Code, Gemini CLI, and any MCP client. Sign in or use an API key. Everything on the platform and the API, with the same output bundle. Schema is available natively in Claude, ChatGPT, Cursor, and any MCP client. Your AI hands Schema raw tables and databases; Schema returns the data understanding, how the sources relate, what is missing, and what can be predicted from it. One remote server, nothing to install: ```text https://mcp.schemalabs.ai/mcp ``` Everything on the platform and the API is available over MCP. A result is the same [output bundle](https://docs.schemalabs.ai/outputs) the API returns. ## Add the server Every client points at the same URL. Pick yours. Claude: ```text Claude.ai, Claude Desktop, Cowork Settings › Connectors › Add custom connector Name Schema Server URL https://mcp.schemalabs.ai/mcp ``` Claude Code: ```bash claude mcp add --transport http schema https://mcp.schemalabs.ai/mcp ``` ChatGPT: ```text ChatGPT Settings › Connectors › Add Server URL https://mcp.schemalabs.ai/mcp ``` Codex: ```toml # ~/.codex/config.toml, then: codex mcp login schema [mcp_servers.schema] url = "https://mcp.schemalabs.ai/mcp" ``` Cursor: ```json // Cursor Settings › Customize › MCP, or .cursor/mcp.json { "mcpServers": { "schema": { "url": "https://mcp.schemalabs.ai/mcp" } } } ``` VS Code: ```json // Command Palette › MCP: Add Server › HTTP, or .vscode/mcp.json { "servers": { "schema": { "type": "http", "url": "https://mcp.schemalabs.ai/mcp" } } } ``` Gemini CLI: ```bash gemini mcp add --transport http schema https://mcp.schemalabs.ai/mcp ``` Any other MCP client: add a remote server with the URL above. Agent frameworks connect the same way; see [Agent frameworks](#agent-frameworks). ## Sign in Sign in with your SchemaLabs account, or use an API key instead, where your client supports it. Your client prompts for whichever it offers on first use. MCP is available on Plus and above. See [Plans](https://docs.schemalabs.ai/billing#plans). ## Use Everything on the platform and the API, available over MCP. Ask in your own words: run Schema on a table, connect a source, create an endpoint, read a report. Inline tables suit small inputs; registered data suits everything else. ## Access Same rules as the API. | Rule | What it means | |---|---| | **Two ways in** | Sign in with your SchemaLabs account, or choose from your API keys. Both are managed and revocable from the platform. | | **Nothing extra to grant** | What your account can do on the platform, it can do here. An API key does what its scopes allow, over MCP exactly as over REST. | | **Usage counts the same way** | Signed-in sessions draw from your plan. API keys bill credits at API rates. Spend caps, usage alerts, and rate limits apply unchanged, and MCP appears on the Usage page as its own line. | | **Your data, handled as before** | The [Data Processing Agreement](https://www.schemalabs.ai/dpa) and [Security](https://www.schemalabs.ai/security) controls apply unchanged. | Which option a client offers is the client's. Claude Code, Cursor, VS Code, Gemini CLI, and the agent frameworks accept either. Claude.ai, Claude Desktop, and Cowork sign in. ChatGPT and Codex sign in only. > **Warning: API keys** Create keys on the platform under **API**, one dedicated, named key per integration, and store each one where your client keeps secrets. Never put a key in a URL, a tool argument, or a prompt. See [Authentication](https://docs.schemalabs.ai/authentication). ## Tools Every platform and API operation is a tool. The list your AI sees depends on the credential: a sign-in covers `read`, `run`, `serve`, and `manage`; the `delete` tools need an API key that carries the `delete` scope. | Scope | Tools | | --- | --- | | `read` | `list_models`, `list_endpoints`, `get_endpoint`, `get_endpoint_logs`, `list_data`, `get_data`, `list_reports`, `get_report`, `list_jobs`, `get_job`, `get_usage` | | `run` | `preflight`, `run` | | `serve` | `serve` | | `manage` | `create`, `refresh`, `upgrade`, `connect_data`, `sync_data`, `pin_data`, `unpin_data`, `generate_data`, `cancel_job` | | `delete` | `delete_endpoint`, `delete_data`, `delete_report` | What to expect: - **Cost before commitment.** `preflight` returns the cell count, the expected time, and what the call would draw, without running anything. Read tools and `preflight` bill nothing. - **Long work returns a job.** Runs that take a while, and every `create`, `refresh`, and `upgrade`, return a job id; your AI checks it with `get_job`. - **Destructive tools ask first.** Delete, cancel, and unpin are marked destructive, so your client confirms with you before running them. - **Sources with credentials belong on the platform.** Register databases and cloud sources on the Data page and refer to them by id. Use `connect_data` for sources that need no credentials. Tool arguments are the API's request fields under the API's names, and a result is the API's response. The [API reference](https://docs.schemalabs.ai/api-reference) documents both. ## Agent frameworks Any framework that consumes MCP servers connects to the same URL. Store the key in the framework's secret store and use a dedicated, named key per integration. CrewAI: ```python import os from crewai import Agent from crewai_tools import MCPServerAdapter server = { "url": "https://mcp.schemalabs.ai/mcp", "transport": "streamable-http", "headers": {"Authorization": f"Bearer {os.environ['SCHEMA_API_KEY']}"}, } with MCPServerAdapter(server) as tools: analyst = Agent(role="data analyst", tools=tools, goal="...", backstory="...") ``` LangChain: ```python import os from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({ "schema": { "url": "https://mcp.schemalabs.ai/mcp", "transport": "streamable_http", "headers": {"Authorization": f"Bearer {os.environ['SCHEMA_API_KEY']}"}, } }) tools = await client.get_tools() # LangChain and LangGraph ``` LlamaIndex: ```python import os from llama_index.tools.mcp import BasicMCPClient, McpToolSpec client = BasicMCPClient( "https://mcp.schemalabs.ai/mcp", headers={"Authorization": f"Bearer {os.environ['SCHEMA_API_KEY']}"}, ) tools = McpToolSpec(client).to_tool_list() ``` | Platform | Where the URL goes | |---|---| | GitHub Copilot | VS Code, see [Add the server](#add-the-server) | | Amazon Bedrock AgentCore | A Gateway target of type MCP, with the key in the outbound authorization header | | Snowflake Cortex agents | An MCP tool source, with the key in the Authorization header | | Databricks | The Schema listing on Databricks Marketplace, Bearer Token authentication, key in the Unity Catalog HTTP connection | | Replit | Integrations › custom MCP server, with the key stored as a Replit secret | | n8n | The MCP Client node in an AI Agent workflow, with a header credential holding the key | To design an agent around the bundle, with your own LLM narrating results, see [Agents and LLMs](https://docs.schemalabs.ai/integrations/agents). ## Troubleshooting | You see | What it means | |---|---| | `401` | The client has no credential, or it expired or was revoked. Sign in again, or check the key. | | `403 insufficient_scope` | The credential lacks the scope named in the message. Use a key with that scope, or a sign-in for anything except delete. | | "needs a Schema subscription" | Sign-in is not available on this organization. Connect with an API key. | | "no prepaid credits on this organization" | A key bills usage credits. Add credits in the platform account menu, or sign in to draw from the plan. | | `429` | A rate or size limit; the message names it. A per-run cap means the same input will not succeed on retry: split the data or create an endpoint. | | A job instead of a result | The call took longer than a synchronous call allows. Ask for the result of that job id, or call `get_job`. | | ChatGPT or Codex asks for a key | ChatGPT and Codex support sign-in only. Use a client that accepts keys if you need one. | More in [Troubleshooting](https://docs.schemalabs.ai/troubleshooting) and [Errors](https://docs.schemalabs.ai/api-reference#errors). Support: [support@schemalabs.ai](mailto:support@schemalabs.ai). ## Next - [Agents and LLMs](https://docs.schemalabs.ai/integrations/agents): Design an agent around the bundle, over MCP or REST. - [Outputs](https://docs.schemalabs.ai/outputs): Every field a tool result contains. - [Authentication](https://docs.schemalabs.ai/authentication): Scopes, keys, and what each one covers. --- # Workflows and data stack Source: https://docs.schemalabs.ai/integrations/data-stack > Call Schema from any HTTP node, schedule syncs and refreshes from your orchestrator, and land bundles in the warehouse. Any tool with an HTTP Request node can call Schema, and any orchestrator can keep an endpoint current. The contract is the same everywhere: `POST /v2/run` for a stateless quick run, `POST /v2/serve/{endpoint_id}` for a live endpoint, a bearer key, JSON in, the bundle out. ## Workflow and no-code tools | Setting | Value | |---|---| | Method | `POST` | | URL | `https://api.schemalabs.ai/v2/serve/{endpoint_id}` (or `/v2/run`) | | Headers | `Authorization: Bearer {{ $secrets.SCHEMA_API_KEY }}`, `Content-Type: application/json` | | Body | `{ "tables": [ { "id": "...", "columns": [...], "rows": [...] } ] }` | Map the incoming item (a form submission, a CRM row, a CSV chunk) into `rows`. Use a `serve`-scoped key for the serve call; the sync, refresh, and job-polling steps below need `manage` and `read`. Then branch on the bundle: - `prediction.results[0].confidence` above your threshold → automate; below → human review. - `tables[0].column_profile[*].pii == true` → mask before the next step. - `cross_table_map.entity_matches[*].confidence` in your review band → review queue. - `imputation.filled[*]` non-empty → write filled values back with their row confidence. Endpoint creation, refresh, and batch runs return `202` with a `job_id`. Add a wait-and-poll loop on `GET /v2/jobs/{job_id}` (2 s, 4 s, 8 s, then every 15 s) and continue on `status == "done"`. Every failure is one JSON envelope with `error.type`; branch on it, and honor `Retry-After` on `429`. See [Errors](https://docs.schemalabs.ai/api-reference#errors). ## Keep the layer current A DAG or workflow that runs after each load: ```text load source → POST /v2/data/:id/sync → POST /v2/endpoints/:id/refresh → poll job → read report ``` Refresh bills only added and changed rows and re-scores held-out; a new report is minted per run so drift is visible in the trail. Use an `Idempotency-Key` per scheduled run so retries never duplicate work. ## Land the bundle in the warehouse For large tables, run in batch and write the output to a warehouse table: ```http POST /v2/run { "data": ["snowflake://example/warehouse/events_2025"], "target": { "column": "converted" }, "options": { "processing": "batch", "out": "warehouse://schema.outputs" } } ``` The bundle (per-table profile, imputation, predictions, unified rows) lands as rows you can join and model on. Batch bills the batch rate. See [Batch and large runs](https://docs.schemalabs.ai/data#batch-and-large-runs). ## dbt Model the bundle tables like any source: `unified_rows` becomes a golden-record model, `imputation.filled` a repair log, `prediction.results` a scored table with the endpoint's `report_id` as lineage. ## Metering Sync itself is free; the pass after it bills only the diff. See [Usage and billing](https://docs.schemalabs.ai/billing). ## Other channels Wherever Schema is consumed outside the direct API (cloud model catalogs, warehouse model pages, orchestration and agent layers), the contract is the same REST API, the same output bundle, and the same meter. For a channel you need, [contact us](https://docs.schemalabs.ai/support). --- # Platform Source: https://docs.schemalabs.ai/platform > The platform at platform.schemalabs.ai, page by page, how each maps to the API, and Chat. The platform is a client of the same API: every button is a REST call, an endpoint created over the API appears in the list immediately, and both surfaces draw one usage meter. ## Pages Nav: **Dashboard** · **Data** · **Quick run** · **Workspace** · **Endpoints** · **Reports** · **Chat** · **API**, with account settings behind your name. | Page | What it does | REST | |---|---|---| | Dashboard | Getting started, the base catalog, org state (endpoints live, held-out at a glance, usage brief) | `GET /v2/models`, `/v2/endpoints`, `/v2/usage` | | Data | Connections and datasets: names, types, rows, sizes, sync state, pin state, synthetic generator. A catalog view; analytical outputs come from runs and appear on Reports | `/v2/data` | | Quick run | The same `POST /v2/run` as the API: one action, full bundle rendered, nothing saved. **Continue in Workspace** carries the configuration over | `POST /v2/run` | | Workspace | Compose an endpoint configuration (data, target, task, base), preview the bundle, publish with **Create endpoint** | `POST /v2/endpoints` on publish | | Endpoints | The stateful objects: serve URL and curl example, pinned data, System Instructions, Chat tab, history, logs, Refresh / Upgrade / Delete | `/v2/endpoints`, `/v2/serve/:id` | | Reports | Every report, newest first, pinned first. Star to pin; **Compare** any two side by side (where the upgrade delta is read) | `/v2/reports` | | Chat | Ask an endpoint's pinned data questions in plain language | platform only | | API | Keys with scopes, endpoint URLs with curl examples, jobs with status and exact cell count, notifications | `/v2/keys`, `/v2/jobs` | Both run surfaces render the bundle exactly as the API returns it, prediction labeled in-context; the held-out badge appears on endpoint results. The cell count is shown before you confirm any job. ## The endpoint page - **Serve URL** with curl example. Serving is REST-only with a `serve`-scoped key. - **Pinned data**: `ds @ dsv` per dataset, with **Refresh on current data**. - **System Instructions**: the endpoint's system-prompt library, edited here. See [System prompts](https://docs.schemalabs.ai/endpoints#system-prompts). - **History**: the report trail. **Logs**: the operation trail, never row data. ## Chat Chat connects an endpoint to an LLM of your choice and answers questions over the pinned dataset. Chat narrates the bundle already computed on the endpoint with your LLM; it runs no inference of its own. You see only the answer, rendered as best fits the question: a sentence, a table, a chart. - **Bring your own LLM key.** Narration runs on your provider account; Schema meters no tokens and no cells for Chat. Sessions are rate-limited. - **Consent is recorded** when you connect a provider, and again before the first send when the endpoint's column profile flags PII. - **Compare**: off, two endpoints, or two LLMs. The same prompt fans out and the answers render side by side. - **System Instructions** are toggleable per conversation; toggling changes nothing on the endpoint. Adding, editing, or deleting a fragment writes the endpoint library and is a config revision. - **Chat answers are narration**: the graded number lives on the endpoint's held-out report. - Requires an endpoint whose dataset is pinned; nothing else. Over REST, call `POST /v2/serve/:id` and narrate the bundle with your own LLM, applying the endpoint's exposed `system_prompts` for parity. See [Agents and LLM frameworks](https://docs.schemalabs.ai/integrations/agents). ## What lives where - **Chat is platform-only**; serving is **REST-only** (humans preview with Quick run or a Workspace preview). - **Account and org administration are platform-only.** - **Notifications** (job completion, usage alerts) arrive in the platform and by email; programmatic consumers poll `GET /v2/jobs/:id` and `GET /v2/usage`. - Plan availability per surface: see [Plans](https://docs.schemalabs.ai/billing#plans). --- # Usage and billing Source: https://docs.schemalabs.ai/billing > The cell meter, the rate card, what each operation costs, plans, rate limits, and storage. One cell = one value = one row x one column. 1 Mcell = 1,000,000 cells. One rate per operation, no input/output split. All prices USD. ```text input_cells C = sum over every table of (rows_t x cols_t) ``` Per-table grids, summed: tables of 100x10 and 50x4 are 1,000 + 200 = 1,200 cells. - **Missing cells bill at the normal rate.** - **C is known before any job runs.** The platform shows the exact draw before you confirm. - **Billing is the cell count.** The meter is rows x columns. **Outputs are free.** Inference operations (quick runs, endpoint creations, endpoint calls) meter inputs only; every pass emits the full bundle, held-out evaluation included, at no per-output charge. Synthetic generation is the one exception: its meter is the cells generated. The cross-table map is never metered separately. ## Rate card (pay-as-you-go API) | Line | Price | Notes | |---|---|---| | Fresh cells, any Schema model | $3.00 / Mcell | one rate per operation; inputs metered, outputs free | | Cached cells (unchanged pinned data re-queried) | $0.30 / Mcell | unchanged data on repeat calls | | Batch (async) | $1.50 / Mcell | elected per job | | Synthetic data generation | $1.50 / Mcell generated | per Mcell generated | | Data storage | $0.20 / GB-month | above plan inclusion, prorated daily | Volume tiers above the listed rates are quoted; see the [pricing page](https://www.schemalabs.ai/pricing). Every Schema model bills at the same rate. Cached, batch, and synthetic are priced per Mcell at the rates above. Endpoint calls are API inference and bill at the same rates as any other pass; an idle endpoint costs nothing. ## What each operation costs Every operation bills at the public rates; subscription allowances draw in proportion to these rates. | Operation | Meter | Rate | |---|---|---| | Quick run (fresh) | `C = sum(rows x cols)`, all tables | $3.00 / Mcell | | Quick run (cached re-use) | unchanged pinned cells re-used | $0.30 / Mcell | | Batch | the op's C | $1.50 / Mcell | | Endpoint create | one fresh pass over the pinned snapshot, held-out included | $3.00 / Mcell once, plus storage | | Endpoint serve | request rows fresh, pinned context cached | fresh + cached rates | | Endpoint refresh | added and changed rows fresh, removals free | fresh + cached rates | | Base upgrade | one fresh pass on the new base | $3.00 / Mcell once | | Synthetic data | `C = cells generated` | $1.50 / Mcell | | Storage | GB-month above inclusion | $0.20 / GB-month; separate monthly line | Failed and cancelled jobs bill nothing; an idempotent retry draws once. A batch job that misses its completion window expires unbilled; resubmit it. The meter counts cells from the first cell, and charges aggregate onto the monthly invoice. **Cached means unchanged.** Added and changed rows bill fresh, unchanged rows bill the cached rate, removed rows bill nothing. Pin state and expiry: [Data and pinning](https://docs.schemalabs.ai/data#pinning). ## Three ways to pay 1. **Pay-as-you-go API.** Metered per Mcell from the first call, no plan required. 2. **Platform subscription.** A weekly cell allowance covering both surfaces; API calls under a subscription key draw the same bucket. Resets every 7 days, no rollover. Usage shows percent used with the reset date and absolute cells consumed. 3. **Add-usage credits.** Prepaid at the public API rates, at par, when the allowance runs out. Optional auto-reload; a separate billing line. Set a monthly spend cap (billing access required to change it); explicit consent is required before the first paid overage. Alerts fire at 80% and 100% of the allowance. ## Plans | | Free | Plus | Pro | Enterprise | |---|---|---|---|---| | Price | $0 | $50/mo | $600/mo | custom | | Usage | One-time limited usage | Expanded usage | 15x usage | Custom usage | | Surfaces | platform only | platform and API | platform and API | platform and API | | Seats | 1 | 1 | 3 | custom | | Storage included | 50 MB | 5 GB | 50 GB | custom | | Batch | none | 2 concurrent jobs | 5 concurrent jobs | custom | | Add-on usage | upgrade to continue | at API rates | at API rates | custom / invoiced | | SLA ([terms](https://www.schemalabs.ai/terms)) | | | Yes | Yes, dedicated CSM | Annual billing saves 15%. Free is a single one-time allotment on the platform; API keys are available from Plus. Enterprise adds the contract-side items: the fine-tune program, dedicated regional deployment on request, contractual SLA. Calls to an endpoint serving a fine-tune bill as ordinary inference. [Contact sales](mailto:info@schemalabs.ai). ## Rate limits Two speed caps, not usage buckets, applied per org, per key, and per endpoint: **requests per minute** and **cells per minute**. Ceilings rise with the plan; when a cap is exceeded the `429` response names it. - **Subscriptions**: ceilings rise with the plan; Enterprise is custom. - **Pay-as-you-go**: the meter and your spend cap set the budget. - **Batch** concurrency per plan: Free 0, Plus 2, Pro 5, Enterprise custom. - Endpoint calls draw the same limiter plus per-endpoint and per-key limits and a concurrency cap. ```http HTTP/1.1 429 Too Many Requests Retry-After: 12 { "error": { "type": "rate_limit", "message": "cells per minute exceeded for org; retry after 12s", "request_id": "req_2e91ac" } } ``` Honor `Retry-After`, then retry with the same `Idempotency-Key`. For sustained throughput, move large fresh passes to batch and pin datasets you re-query. ## Storage Pinned datasets and stored results count toward plan storage. Overage bills $0.20/GB-month, prorated daily, as a monthly line separate from cell credits; Free hard-stops at 50 MB. What is stored and when it is deleted: [Data](https://docs.schemalabs.ai/data#persistence-follows-the-operation). > **Note:** The current rate card and plans are at [schemalabs.ai/pricing](https://www.schemalabs.ai/pricing); changes are announced in the [changelog](https://docs.schemalabs.ai/changelog). --- # Changelog Source: https://docs.schemalabs.ai/changelog > Product and API changes for Schema, newest first: bases, routes, and docs. ## September 13, 2026: MCP server 1.0.0 Schema is available natively in MCP clients: a remote server at `https://mcp.schemalabs.ai/mcp` with every API operation as a tool, sign-in or API key. Tool names, descriptions, schemas and annotations are versioned; a change to any of them is a new minor version. See [MCP server](https://docs.schemalabs.ai/mcp). ## August 2026 ### Schema-2 available (August 13, 2026) - `schema-2` is the latest base and the default for new endpoints. All three prediction task types (classification, regression, anomaly) are available on it. Model card: [Schema-2](https://www.schemalabs.ai/model-card/schema-2). Announcement: [Introducing Schema-2](https://www.schemalabs.ai/company/news/introducing-schema-2). - Lifecycle status for `schema-1` is on its [model card](https://www.schemalabs.ai/model-card). Move endpoints with `POST /v2/endpoints/:id/upgrade { "base": "schema-2" }`. - No rate change: every Schema model bills at the same rate. ### API - Endpoints as the persistent object: `POST /v2/endpoints`, `POST /v2/serve/:id`, `refresh`, `upgrade`, logs, delete. - Stateless `POST /v2/run` with multi-table input and the cross-table map. - Data family: connect, list, get, sync, pin, unpin, generate, delete. - Reports, jobs, usage, keys. - Idempotency keys on run, create, refresh, upgrade, generate. Cursor pagination on large arrays. One error envelope. > **Note:** Bundle additions are announced here and are non-breaking. Renames or removals happen only at a new API version. --- # Troubleshooting Source: https://docs.schemalabs.ai/troubleshooting > Every response the Schema API returns when something needs your attention, what it is telling you, and the concrete next step. Every non-2xx response carries one envelope with a machine-readable `error.type` and a `request_id` (see [Errors](https://docs.schemalabs.ai/api-reference#errors)). Branch on the type; quote the `request_id` to [support](https://docs.schemalabs.ai/support). ## What does each response mean? | What you see | What to do | |---|---| | `400 validation_error` on `POST /v2/serve/:id` | The message names the table or column that differs from the pinned snapshot. Send table ids and columns that match the endpoint's data, or sync the source and refresh the endpoint onto the new schema. | | `401 auth_error` | The key is missing, malformed, or revoked. Create a key on the platform's API page and send it as `Authorization: Bearer`. | | `403 auth_error` | The key lacks the route's scope. Use a key with `read`, `run`, `serve`, `manage`, or `delete` as the route requires (see [Authentication](https://docs.schemalabs.ai/authentication)). | | `404 not_found` | Check the id. Routes accept an endpoint id or its name; only the id survives a rename. | | `409 conflict` with `running_job_id` | A refresh or upgrade is already running on that endpoint. Wait for the job, or cancel it with `POST /v2/jobs/:id/cancel`, then resubmit. | | `409 conflict` on a retried POST | The `Idempotency-Key` was reused with a different body. Use a new key for a new request. | | `422 capability_unavailable` | The base named in the request has been retired. Omit `base` to use the latest model, or move the endpoint with `POST /v2/endpoints/:id/upgrade` (see [Models](https://docs.schemalabs.ai/models)). | | `429 rate_limit` | Wait for `Retry-After` and retry. Send large passes with `"options": { "processing": "batch" }` (see [Rate limits](https://docs.schemalabs.ai/billing#rate-limits)). | | Job `status: "failed"` | Read `error` on the job object and resubmit. Failed jobs bill nothing. | | Job `status: "expired"` | A batch job missed its completion window. Resubmit as batch, or with the default `realtime` processing; expired jobs bill nothing. | ## What does each result mean? | What you see | What to do | |---|---| | `held_out.low_confidence: true` | `low_confidence_reason` names the cause: a small test partition or a target that is mostly missing. Add rows, or name a target with more values, then refresh. See [Held-out reports](https://docs.schemalabs.ai/held-out). | | `target_selection` picked a column you did not intend | Set `target.column` (as `table.column`) and, for a numeric target, `task.type: "regression"`. On a text-only table, always name the target. | | `prediction: null` | The request used `target.mode: "none"`, the understanding-only configuration. Set a target to get the prediction slice. | | `held_out: null` on a prediction | A quick run prediction is in-context and carries `held_out: null`. Create an endpoint on the same configuration for a scored, servable prediction. | | Few or no `entity_matches` | The tables need to describe the same entities and share at least one attribute (a phone, a name, a date). Lower `options.confidence_threshold` to see lower-confidence candidates, and check the `column_alignment` list first. | | A `sector` you did not expect | Read `sector.top5` and the confidence on each entry. Detection is sector-agnostic and understands the values in the table. | | An imputed value looks wrong | `imputation.row_confidence` scores each filled row. Gate on it in your pipeline, and keep the original cells; imputation never overwrites your data. | --- # Glossary Source: https://docs.schemalabs.ai/glossary > The product vocabulary, one entry per term, identical across the API and the platform. ## Product objects **Data Language Model (DLM)**: a foundation model that understands tables natively, from raw cell values, and returns understanding, relationships, missing values, and predictions in one pass. Schema models are DLMs. **Base**: a Schema model generation, named `schema-{n}` (`s{n}` in report names). Runs and new endpoints use the latest base unless you name one. [Models](https://docs.schemalabs.ai/models). **Quick run**: `POST /v2/run`. A stateless pass over the tables in the request that returns the full bundle and persists nothing. Its prediction is labeled `tier: "in_context"`. [Quick run](https://docs.schemalabs.ai/api-reference/run). **Endpoint**: your persistent object: a base, pinned dataset snapshots, a configuration (target, task, thresholds), a library of system prompts, and a held-out report, served at a stable URL. [Endpoints](https://docs.schemalabs.ai/endpoints). **Serve**: `POST /v2/serve/:id`. Live inference over the request tables, scored against the endpoint's pinned data, returning the bundle. Needs the `serve` scope. **Refresh**: `POST /v2/endpoints/:id/refresh`. Re-syncs the pinned snapshot, scores held-out on a fresh split, and mints a new report. The only operation that makes an endpoint stop reflecting removed rows. **Upgrade**: `POST /v2/endpoints/:id/upgrade`. Re-runs the same pinned data on a newer base and mints a new report. **System prompt**: a named instruction fragment (`sp_...`) on an endpoint that steers an LLM. Executed in the platform's Chat; exposed on the endpoint object so your own LLM can apply it. **Job**: an async unit of work (`job_...`) for endpoint creation, refresh, upgrade, synthetic generation, and large or batch runs. Poll `GET /v2/jobs/:id`. [Jobs](https://docs.schemalabs.ai/api-reference/jobs). **Batch**: a service tier elected per job with `"options": { "processing": "batch" }`. Runs with a completion window and bills the batch rate ($1.50 / Mcell). **Scope**: what an API key may do: `read`, `run`, `serve`, `manage`, `delete`. [Authentication](https://docs.schemalabs.ai/authentication). ## Data **Connection and dataset**: a connection (`conn_...`) is where data comes from; a dataset (`ds_...`) is an ingested or registered table. Both live under `/v2/data`. **Snapshot**: an immutable copy of a dataset minted by each ingest or sync, id `dsv_...`. Endpoints pin snapshots and held-out reports stamp them. **Pinned**: a dataset state. Pinned data serves repeat calls at the cached rate indefinitely and counts toward plan storage. Endpoint creation pins its datasets automatically. [Data](https://docs.schemalabs.ai/data#pinning). ## Outputs **Bundle**: the one output shape every run, creation, and serve returns: sector, column profile, cross-table map, missing-value imputation, and predictions, each with its own confidence. [Outputs](https://docs.schemalabs.ai/outputs). **Pass**: one inference over a set of tables. Every pass emits the full bundle; there is one rate per pass, with no per-output charge. **In-context**: how every prediction is made: from the labeled rows present (the request's, or the endpoint's pinned data). Nothing trains. **Target and task**: the column to predict and the prediction type (`classification`, `regression`, `anomaly`). Both default to `auto`; the response states what was chosen and why. **Sector**: the industry or domain a table belongs to, returned as `top1` and `top5` with a confidence each. Detection is sector-agnostic: it understands the values, not the column names. **Column profile**: the per-column understanding: `role`, `role_confidence`, `type`, `missing_pct`, `pii`. **Cross-table map**: how two or more tables relate without shared keys: column alignments, entity matches, and a unified schema, each with a confidence. **Column alignment**: a pair of columns from two tables that carry the same attribute, with its `confidence` and a verbatim `example` from each side. **Entity match**: a pair of rows from two tables that describe the same real-world entity, with the attributes it was matched on. **Unified rows**: the cross-table map materialized into merged records, one per matched entity, with provenance per field and the raw variants of shared values. **Missing-value imputation**: filled values for empty cells, numeric and categorical, with a row-level confidence. Original cells are never overwritten. ## Trust **Held-out**: the score on rows whose target values the model did not see when predicting them, measured by an in-context split at creation, refresh, and upgrade, stated with its split, seed, baseline, and snapshot. [Held-out reports](https://docs.schemalabs.ai/held-out). **Report**: the immutable trust artifact minted by each create, refresh, and upgrade. Id `r_...`, display name `{endpoint}.{base}.{op}.{YYYY-MM-DD}`. [Reports](https://docs.schemalabs.ai/api-reference/reports). ## Metering **Cell**: one value, one row by one column. The metering unit. 1 Mcell = 1,000,000 cells. Missing cells count like any other cell. [Usage and billing](https://docs.schemalabs.ai/billing). **Fresh and cached**: fresh cells are understood for the first time and bill the fresh rate ($3.00 / Mcell); cached cells are unchanged pinned data re-used on a later pass and bill the cached rate ($0.30 / Mcell). --- # Support Source: https://docs.schemalabs.ai/support > How to reach us, what to include, and where the trust and legal information lives. ## Channels | Need | Contact | |---|---| | Technical and API questions | [support@schemalabs.ai](mailto:support@schemalabs.ai) | | Compliance and customer documentation | [compliance@schemalabs.ai](mailto:compliance@schemalabs.ai) | | Security and responsible disclosure | [security@schemalabs.ai](mailto:security@schemalabs.ai) · [Responsible disclosure](https://www.schemalabs.ai/responsible-disclosure) | | Legal | [legal@schemalabs.ai](mailto:legal@schemalabs.ai) | | General, Enterprise, fine-tune program, dedicated regional deployment | [info@schemalabs.ai](mailto:info@schemalabs.ai) | | Media | [media@schemalabs.ai](mailto:media@schemalabs.ai) | Support tiers by plan: docs and community (Free), email (Plus), priority email with SLA (Pro), dedicated CSM (Enterprise). ## What to include - The `request_id` from the error envelope or the endpoint logs. - The endpoint ID or `job_id`, never the row data. - The `error.type` and message. - Timestamps in UTC. ## Trust and legal [Trust Center](https://www.schemalabs.ai/trust) · [Security](https://www.schemalabs.ai/security) · [Privacy](https://www.schemalabs.ai/privacy) · [Terms](https://www.schemalabs.ai/terms) · [Use Policy](https://www.schemalabs.ai/use-policy) · [Model License](https://www.schemalabs.ai/model-license) · [Legal index](https://www.schemalabs.ai/legal) --- # API reference Source: https://docs.schemalabs.ai/api-reference > REST reference for the Schema API: base URL, authentication, errors, idempotency, async jobs, pagination, identifiers, and every route. The Schema API is a REST API over JSON. Every route lives under `/v2/`, authenticates with an org-scoped API key, and returns one error envelope on failure. What you create over the API appears in the platform immediately, and both draw one usage meter. One stateless call, the quick run (`POST /v2/run`), and one stateful object, the endpoint (`create`, `serve`, `refresh`, `upgrade`). Every pass emits the full output bundle; outputs are free. ## Basics - **Base URL**: `https://api.schemalabs.ai` - **Version**: all routes under `/v2/`. A breaking change mints `/v3`; adding fields to the bundle is non-breaking and announced. Tolerate unknown fields. Base models (`schema-{n}`) and endpoint state are separate version axes. - **Auth**: `Authorization: Bearer $SCHEMA_API_KEY`. Keys are org-scoped with operation scopes (`read`, `run`, `serve`, `manage`, `delete`). See [Authentication](https://docs.schemalabs.ai/authentication). - **Content type**: `application/json` in and out. Timestamps are ISO 8601 UTC. Large data enters through datasets and connections, not request bodies. - **OpenAPI**: every live endpoint publishes its own description at `https://api.schemalabs.ai/v2/openapi/{endpoint_id}`, importable into any tool that reads OpenAPI. See [Endpoint OpenAPI description](https://docs.schemalabs.ai/api-reference/endpoints#endpoint-openapi). - **Rate limits**: requests per minute and cells per minute, per org and per key. Exceeding returns `429` with `Retry-After`. See [Rate limits](https://docs.schemalabs.ai/billing#rate-limits). - **Metering**: cells (rows x columns), summed per table. Inputs metered, outputs free. See [Usage and billing](https://docs.schemalabs.ai/billing). ## Errors Every non-2xx response returns one envelope: `{ "error": { "type", "message", "param", "request_id" } }`. The `type` is one of eight machine-readable strings, `param` points at the offending field when there is one, and `request_id` is what to quote to support. A run, creation, or serve either returns the full bundle or fails as `job_failed`: every success is a complete bundle, and failed jobs bill nothing. | type | HTTP | When | |---|---|---| | `validation_error` | 400 | A field is malformed, or data POSTed to an endpoint does not match its pinned snapshot’s schema (the message names the divergence). | | `auth_error` | 401 / 403 | Missing or invalid key (401), or the key lacks the route’s scope (403). A 403 carries `WWW-Authenticate: Bearer error="insufficient_scope", scope="…"` naming the scope the route needs; the message points to the API keys page. | | `not_found` | 404 | Unknown endpoint, dataset, report, job, or key id; or a deleted endpoint’s URL. | | `conflict` | 409 | An `Idempotency-Key` reused with a different body; a second mutating job on an endpoint that already has one running (the envelope carries `running_job_id`); unpinning or deleting a dataset a live endpoint depends on (the message names the endpoint). | | `capability_unavailable` | 422 | The request names a retired base: a run, creation, or serve on it. Omit `base` for the latest model, or upgrade the endpoint. | | `rate_limit` | 429 | Requests per minute or cells per minute exceeded, per org, per key, or per endpoint. `Retry-After` states when to retry. | | `job_failed` | 500 | An async job could not complete. The job object carries the error message. Nothing is billed. | | `internal` | 5xx | Something on our side. Retry with backoff and quote the `request_id` if it persists. | ### Handling errors Branch on `error.type`, not on the message. Retry `rate_limit` after `Retry-After` and `internal` with exponential backoff; retried POSTs are safe when you send an `Idempotency-Key`. Treat `conflict` on refresh or upgrade as "wait for `running_job_id`". Surface `validation_error.param` to whoever owns the request payload. ## Idempotency `POST /v2/run`, `POST /v2/endpoints`, `refresh`, `upgrade`, and `POST /v2/data/generate` accept an `Idempotency-Key` header: any string unique per logical request (a UUID is the usual choice), reused on every retry of that request. A retried POST with the same key and body returns the original result or job; the same key with a different body is `409 conflict`. Send one on every expensive submit, especially from CI and retrying clients. ## Async jobs Endpoint creation, refresh, upgrade, synthetic generation, and large or batch quick runs return `202` with a `job_id`. Poll `GET /v2/jobs/:id`, or rely on the platform notification and email on completion. Status flow: `queued` -> `running` -> `done` | `failed` | `cancelled` | `expired`. - The endpoint id is assigned at submit, so `GET /v2/endpoints` lists it immediately as `creating`; it flips to `live` when the job completes. - A job either completes with its full result or fails as `job_failed`. Failed and cancelled jobs bill nothing; a batch job that misses its completion window expires unbilled. - `POST /v2/jobs/:id/cancel` stops a queued or running job. A cancelled creation stops the creation, and the endpoint can be deleted or created again; a cancelled refresh or upgrade leaves the endpoint serving its previous state. ## Pagination Large arrays (`unified_rows.rows`, `prediction.results`, `imputation.filled`) return a first page inline (`sample_shown`) plus a cursor; `total` is always the full count. Full retrieval is cursor pagination (`?cursor=...&limit=...`) over the endpoint or report resource, or `options.out` on a large quick run to write the bundle to a file or warehouse table. Lists return the resource array (`endpoints`, `datasets`, `reports`, `jobs`), `next_cursor`, and `total`; `GET /v2/keys` returns the full list. Data POSTed to an endpoint is validated against the schema of its pinned snapshots; a mismatch is a `400 validation_error` naming the divergence. ## Identifiers One canonical scheme: the endpoint ID is the one unprefixed id; every other resource carries a prefix. Immutable ids never change on rename, refresh, or upgrade; an endpoint name is a mutable alias, and routes accept either. Surfaces truncate the endpoint ID to its first 8 characters for display; the full ID is the identifier. Snapshots, reports, and ids are immutable once minted. | Resource | Formulation | Mutable | Example | |---|---|---|---| | Endpoint | opaque unique ID, assigned at creation | immutable | `{endpoint_id}` | | Endpoint name | lowercase slug, user-chosen | mutable (rename) | `churn` | | Base | `schema-{n}`; abbreviated `s{n}` in report names | n/a | `schema-2, s2` | | Report id | `r_{hex}` | immutable | `r_8f3a` | | Report name | `{endpoint}.{base}.{op}.{YYYY-MM-DD}` | reflects the name at mint | `churn.s2.upgrade.2026-08-14` | | Quick run | `run_{id}` | immutable | `run_9f2a4c` | | Job | `job_{id}` | immutable | `job_9a5b01d4` | | Connection | `conn_{id}` plus a scheme ref | immutable | `conn_3f9a, snowflake://…` | | Dataset | `ds_{id}` | immutable | `ds_crm01` | | Dataset snapshot | `dsv_{id}`, minted per ingest or sync | immutable | `dsv_91f2` | | API key id | `key_{id}` | immutable | `key_77ab` | | API key secret | `sk_live_{rand}` | shown once | `sk_live_…` | | System prompt fragment | `sp_{id}` (body versioned) | immutable id | `sp_12` | | Request | `req_{id}` in every error envelope and log | n/a | `req_e7f04a` | ## All routes | Method | Path | Does | Scope | Reference | |---|---|---|---|---| | `POST` | `/v2/preflight` | Preflight a call | `run` | [Quick run](https://docs.schemalabs.ai/api-reference/run#preflight) | | `POST` | `/v2/run` | Create a quick run | `run` | [Quick run](https://docs.schemalabs.ai/api-reference/run#create-run) | | `POST` | `/v2/data/connect` | Connect a source | `manage` | [Data](https://docs.schemalabs.ai/api-reference/data#connect-data) | | `GET` | `/v2/data` | List data | `read` | [Data](https://docs.schemalabs.ai/api-reference/data#list-data) | | `GET` | `/v2/data/:id` | Retrieve data | `read` | [Data](https://docs.schemalabs.ai/api-reference/data#retrieve-data) | | `POST` | `/v2/data/:id/sync` | Sync a connection | `manage` | [Data](https://docs.schemalabs.ai/api-reference/data#sync-data) | | `POST` | `/v2/data/:id/pin` | Pin a dataset | `manage` | [Data](https://docs.schemalabs.ai/api-reference/data#pin-data) | | `POST` | `/v2/data/:id/unpin` | Unpin a dataset | `manage` | [Data](https://docs.schemalabs.ai/api-reference/data#unpin-data) | | `POST` | `/v2/data/generate` | Generate synthetic data | `manage` | [Data](https://docs.schemalabs.ai/api-reference/data#generate-data) | | `DELETE` | `/v2/data/:id` | Delete data | `delete` | [Data](https://docs.schemalabs.ai/api-reference/data#delete-data) | | `POST` | `/v2/endpoints` | Create an endpoint | `manage` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#create-endpoint) | | `GET` | `/v2/endpoints` | List endpoints | `read` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#list-endpoints) | | `GET` | `/v2/endpoints/:id` | Retrieve an endpoint | `read` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#retrieve-endpoint) | | `POST` | `/v2/serve/:id` | Serve an endpoint | `serve` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#serve-endpoint) | | `POST` | `/v2/endpoints/:id/refresh` | Refresh an endpoint | `manage` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#refresh-endpoint) | | `POST` | `/v2/endpoints/:id/upgrade` | Upgrade an endpoint | `manage` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#upgrade-endpoint) | | `GET` | `/v2/endpoints/:id/logs` | Endpoint logs | `read` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#endpoint-logs) | | `GET` | `/v2/openapi/:id` | Endpoint OpenAPI description | `read` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#endpoint-openapi) | | `DELETE` | `/v2/endpoints/:id` | Delete an endpoint | `delete` | [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints#delete-endpoint) | | `GET` | `/v2/reports` | List reports | `read` | [Reports](https://docs.schemalabs.ai/api-reference/reports#list-reports) | | `GET` | `/v2/reports/:id` | Retrieve a report | `read` | [Reports](https://docs.schemalabs.ai/api-reference/reports#retrieve-report) | | `GET` | `/v2/jobs` | List jobs | `read` | [Jobs](https://docs.schemalabs.ai/api-reference/jobs#list-jobs) | | `GET` | `/v2/jobs/:id` | Retrieve a job | `read` | [Jobs](https://docs.schemalabs.ai/api-reference/jobs#retrieve-job) | | `POST` | `/v2/jobs/:id/cancel` | Cancel a job | `manage` | [Jobs](https://docs.schemalabs.ai/api-reference/jobs#cancel-job) | | `GET` | `/v2/models` | List models | `read` | [Models](https://docs.schemalabs.ai/api-reference/models#list-models) | | `GET` | `/v2/usage` | Retrieve usage | `read` | [Usage](https://docs.schemalabs.ai/api-reference/usage#retrieve-usage) | | `GET` | `/v2/keys` | List keys | `read` | [API keys](https://docs.schemalabs.ai/api-reference/keys#list-keys) | | `POST` | `/v2/keys` | Create a key | `manage` | [API keys](https://docs.schemalabs.ai/api-reference/keys#create-key) | | `POST` | `/v2/keys/:id/rotate` | Rotate a key | `manage` | [API keys](https://docs.schemalabs.ai/api-reference/keys#rotate-key) | | `DELETE` | `/v2/keys/:id` | Revoke a key | `delete` | [API keys](https://docs.schemalabs.ai/api-reference/keys#revoke-key) | ## Resources - [Quick run](https://docs.schemalabs.ai/api-reference/run.md): Run Schema over the tables in one request: a stateless pass that returns the full output bundle, the same call the platform's Quick run page makes. - [Data](https://docs.schemalabs.ai/api-reference/data.md): Register connections and datasets under /v2/data, sync snapshots, pin data for the cached rate, generate synthetic tables, and delete what you no longer need. - [Endpoints](https://docs.schemalabs.ai/api-reference/endpoints.md): 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. - [Reports](https://docs.schemalabs.ai/api-reference/reports.md): List and retrieve held-out reports: the immutable trust artifact minted by every endpoint create, refresh, and upgrade, with its split, baseline, and snapshot. - [Jobs](https://docs.schemalabs.ai/api-reference/jobs.md): List, poll, and cancel the async jobs behind endpoint creation, refresh, upgrade, synthetic data generation, and batch quick runs. - [Models](https://docs.schemalabs.ai/api-reference/models.md): List the Schema models available to your organization, which one is the default, and any Enterprise fine-tune you own. - [Usage](https://docs.schemalabs.ai/api-reference/usage.md): Read your organization's usage meter: cells drawn per surface and per operation, percent of the weekly allowance used, and add-usage settings. - [API keys](https://docs.schemalabs.ai/api-reference/keys.md): List, create, rotate, and revoke org-scoped API keys carrying the read, run, serve, manage, and delete scopes. --- # Quick run Source: https://docs.schemalabs.ai/api-reference/run > Run Schema over the tables in one request: a stateless pass that returns the full output bundle, the same call the platform's Quick run page makes. A quick run is the one stateless call in the product: one pass over the tables you send, returning the full output bundle in one response. Nothing is persisted, no dataset is registered, and the prediction is in-context, labeled `held_out: null`. The platform’s Quick run page is this same call. Use a quick run for one-shot analysis, agents meeting a new source mid-task, and any call that wants the bundle and nothing kept. When you want a scored, servable layer that holds your pinned data and follows it over time, [create an endpoint](https://docs.schemalabs.ai/api-reference/endpoints#create-endpoint). ## Operations ### Preflight a call `POST /v2/preflight` (scope: `run`) Returns the exact cell count, the draw in the caller's bucket, the predicted wall time and any refusal a run, serve or generate call would meet, before any job starts. Runs no inference and bills nothing. Send the body of the call you are about to make with `op` naming it: `run`, `serve` (with `endpoint`) or `generate`. The response counts cells per table and in total, applies the operation multiplier, and reports `expected_seconds` from the measured timing model. `draw` follows the credential: a signed-in session sees the allowance bucket (percent used and cells remaining, never the allowance size); an API key sees the prepaid-credit cost in USD. `refusal` is null when the real call would go through, otherwise the same message the real call would return: per-run cap, inline run cap (a run without `out` above 1,000,000 cells), allowance shortfall, or no prepaid credits on a key. **Body** (application/json) - `op` (string, required): `run`, `serve` or `generate`. - `endpoint` (string): For `op: serve`, the endpoint id or name. - `tables` (array of objects): Any number of tables, inline. Two or more produce the cross-table map. Large data should be referenced by dataset or connection instead of sent inline (see `data`). - `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. - `data` (array of strings): Dataset ids (`ds_...`) or connection refs (`snowflake://sales/live`, `s3://bucket/path`) to run on instead of, or in addition to, inline `tables`. Using a dataset or connection in a run registers nothing. - `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. - `processing` (string, default realtime): `batch` bills the job at the batch rate and runs with a completion window shown on the job’s eta. One of: `realtime`, `batch`. - `out` (string): For large runs: write the bundle to a file or warehouse table instead of returning it inline, for example `warehouse://schema.outputs`. **Returns** `200` with the counts; the call itself is never made. **Response fields** - `cells` (integer): Total cells the call would draw before the multiplier. - `tables` (array): Per-table cell counts. - `op` (string): The operation checked. - `multiplier` (number): Operation multiplier (batch and synthetic draw at reduced rates). - `fresh_cell_equivalents` (integer): Cells after the multiplier: what the meter records. - `expected_seconds` (integer): Predicted wall time for the real call. Clients that hold synchronous calls read this. - `draw` (object): `bucket` (`allowance` or `prepaid_credits`) with `sufficient`; `percent_used` and `cells_remaining` for the allowance, `credit_usd` for credits. - `refusal` (string): The refusal the real call would meet, or null. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/preflight' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "cells": 12, "tables": [ { "id": "inline", "cells": 12 } ], "op": "run", "multiplier": 1, "fresh_cell_equivalents": 12, "expected_seconds": 1, "draw": { "bucket": "prepaid_credits", "credit_usd": 0.000036, "sufficient": true }, "refusal": null, "request_id": "req_1f3a" } ``` ### Create a quick run `POST /v2/run` (scope: `run`) Runs one stateless pass over any number of tables and returns the full bundle: sector, column profile, cross-table map (2+ tables), missing-value imputation, and an in-context prediction. Send any number of tables from any number of sources: inline, or by dataset and connection id. Every output carries its own confidence. Two or more tables produce the cross-table map and unified rows; a single table returns `null` for both. Large quick runs execute as async jobs: the response is `202` with a `job_id`, and the bundle is retrievable from the job when it completes, or written to `options.out`. Set `options.processing` to `"batch"` to bill at the batch rate; batch jobs run with a completion window shown on the job. A run without `out` returns its bundle inline and is capped at 1,000,000 cells on every tier; above that, pass `out` to write the full bundle to a file or warehouse table, or create an endpoint. Both checks run at preflight. **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) - `tables` (array of objects): Any number of tables, inline. Two or more produce the cross-table map. Large data should be referenced by dataset or connection instead of sent inline (see `data`). 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. - `data` (array of strings): Dataset ids (`ds_...`) or connection refs (`snowflake://sales/live`, `s3://bucket/path`) to run on instead of, or in addition to, inline `tables`. Using a dataset or connection in a run registers nothing. - `base` (string, default latest): The Schema model to run on. Defaults to the latest (see `GET /v2/models`). - `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. - `processing` (string, default realtime): `batch` bills the job at the batch rate and runs with a completion window shown on the job’s eta. One of: `realtime`, `batch`. - `out` (string): For large runs: write the bundle to a file or warehouse table instead of returning it inline, for example `warehouse://schema.outputs`. **Returns** The output bundle, `200`; large quick runs and batch jobs return `202` with a job. **Response fields** - `mode` (string): Always `"run"`. - `run_id` (string): Identifier of this quick run, for support and logs. The run’s job and run record appear in your org’s jobs and reports listings; submitted rows are discarded after the pass. - `base` (string): The base that produced the bundle. - `feed` (string): `single_table` or `multi_table`. - `status` (string): `complete`, or `queued` / `running` on a `202`. - `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`. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/run' \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Idempotency-Key: " \ -H "Content-Type: application/json" \ -d '{ "tables": [ { "id": "crm_contacts", "columns": ["full_name", "phone", "email", "signup_date"], "rows": [ ["Ana Kaya", "555-0142", "ana.k@example.com", "2024-11-03"], ["Devraj Nair", "555-0199", "d.nair@example.com", "2024-12-01"], ["Mira Ostrom", "555-0210", null, "2025-01-17"] ] }, { "id": "cards_db", "columns": ["holder", "phone_e164", "card_brand", "credit_limit"], "rows": [ ["KAYA, ANA", "+1 555 0142", "Visa", 12000], ["NAIR, DEVRAJ", "+1 555 0199", "Mastercard", 6500], ["OSTROM, MIRA", "+1 555 0210", "Amex", null] ] } ], "target": { "mode": "auto" }, "task": { "mode": "auto" } }' ``` **Example response (200)** ```json { "mode": "run", "run_id": "run_9f2a4c", "base": "schema-2", "feed": "multi_table", "status": "complete", "summary": { "tables": 2, "records_linked": 3, "records_total": 3, "keys_used": "none", "shared_attributes": 2, "prediction": { "target": "cards_db.card_brand", "task": "classification", "tier": "in_context", "held_out": null } }, "tables": [ { "id": "crm_contacts", "rows": 3, "cols": 4, "sector": { "top1": { "name": "consumer financial services", "confidence": 0.89 }, "top5": [ { "name": "consumer financial services", "confidence": 0.89 }, { "name": "customer relationship management", "confidence": 0.74 }, { "name": "credit card issuing", "confidence": 0.55 }, { "name": "marketing services", "confidence": 0.41 }, { "name": "retail banking", "confidence": 0.33 } ] }, "column_profile": [ { "name": "full_name", "role": "name", "role_confidence": 0.93, "type": "categorical", "missing_pct": 0, "pii": true }, { "name": "phone", "role": "phone", "role_confidence": 0.97, "type": "categorical", "missing_pct": 0, "pii": true }, { "name": "email", "role": "email", "role_confidence": 0.98, "type": "categorical", "missing_pct": 0.33, "pii": true }, { "name": "signup_date", "role": "date", "role_confidence": 0.95, "type": "datetime", "missing_pct": 0, "pii": false } ] }, { "id": "cards_db", "rows": 3, "cols": 4, "sector": { "top1": { "name": "credit card issuing", "confidence": 0.91 }, "top5": [ { "name": "credit card issuing", "confidence": 0.91 }, { "name": "consumer financial services", "confidence": 0.8 }, { "name": "retail banking", "confidence": 0.47 }, { "name": "payments processing", "confidence": 0.39 }, { "name": "consumer lending", "confidence": 0.31 } ] }, "column_profile": [ { "name": "holder", "role": "name", "role_confidence": 0.91, "type": "categorical", "missing_pct": 0, "pii": true }, { "name": "phone_e164", "role": "phone", "role_confidence": 0.98, "type": "categorical", "missing_pct": 0, "pii": true }, { "name": "card_brand", "role": "category", "role_confidence": 0.94, "type": "categorical", "missing_pct": 0, "pii": false }, { "name": "credit_limit", "role": "measure", "role_confidence": 0.96, "type": "numeric", "missing_pct": 0.33, "pii": false } ] } ], "cross_table_map": { "summary": { "tables": 2, "shared_attributes": 2, "entities_matched": 3, "columns_only_in_a": 2, "columns_only_in_b": 2, "keys_used": "none" }, "column_alignment": [ { "a": "crm_contacts.phone", "b": "cards_db.phone_e164", "attribute": "phone", "confidence": 0.96, "example": { "a": "555-0142", "b": "+1 555 0142" } }, { "a": "crm_contacts.full_name", "b": "cards_db.holder", "attribute": "name", "confidence": 0.9, "example": { "a": "Ana Kaya", "b": "KAYA, ANA" } } ], "entity_matches": [ { "a_row": 0, "b_row": 0, "confidence": 0.94, "matched_on": [ "phone", "name" ] }, { "a_row": 1, "b_row": 1, "confidence": 0.92, "matched_on": [ "phone", "name" ] }, { "a_row": 2, "b_row": 2, "confidence": 0.91, "matched_on": [ "phone", "name" ] } ], "unified_schema": { "shared": [ { "attribute": "phone", "from": [ "crm_contacts.phone", "cards_db.phone_e164" ] }, { "attribute": "name", "from": [ "crm_contacts.full_name", "cards_db.holder" ] } ], "a_only": [ "crm_contacts.email (email)", "crm_contacts.signup_date (date)" ], "b_only": [ "cards_db.card_brand (category)", "cards_db.credit_limit (measure)" ] } }, "unified_rows": { "schema": [ "name", "phone", "email", "signup_date", "card_brand", "credit_limit" ], "rows": [ { "entity": 0, "from": { "a_row": 0, "b_row": 0 }, "confidence": 0.94, "record": { "name": "Ana Kaya", "phone": "555-0142", "email": "ana.k@example.com", "signup_date": "2024-11-03", "card_brand": "Visa", "credit_limit": 12000 }, "shared_variants": { "phone": { "crm_contacts.phone": "555-0142", "cards_db.phone_e164": "+1 555 0142" } } } ], "total": 3, "sample_shown": 1 }, "imputation": { "filled": [ { "table": "cards_db", "row": 2, "column": "credit_limit", "value": 8400, "method": "schema-2" } ], "row_confidence": [ { "table": "cards_db", "row": 2, "confidence": 0.81 } ], "total_filled": 1 }, "target_selection": { "mode": "auto", "column": "cards_db.card_brand", "reason": "most predictable target among the eligible categorical columns", "overridable": true, "candidates": [ { "col": "cards_db.card_brand", "selected": true, "eligible": true, "reason": "most predictable from the other columns" }, { "col": "cards_db.credit_limit", "eligible": false, "reason": "numeric measure, not a category" }, { "col": "crm_contacts.email", "eligible": false, "reason": "looks like an identifier (mostly unique values)" } ] }, "task_selection": { "mode": "auto", "type": "classification", "reason": "categorical target (card_brand)", "overridable": true }, "prediction": { "target": "cards_db.card_brand", "task_type": "classification", "tier": "in_context", "classes": [ "Amex", "Mastercard", "Visa" ], "dropped_rows": { "missing_target": 0 }, "results": [ { "row": 0, "label": "Visa", "confidence": 0.94, "probabilities": { "Amex": 0.01, "Mastercard": 0.05, "Visa": 0.94 } } ], "note": "in-context prediction; create an endpoint for a held-out score." } } ``` **Prediction slice, regression (task.type "regression") (200)** ```json { "prediction": { "target": "cards_db.credit_limit", "task_type": "regression", "tier": "in_context", "results": [ { "row": 0, "value": 12480, "median": 12010, "std": 3120, "quantiles": { "0.1": 8400, "0.5": 12010, "0.9": 17900 } } ] } } ``` **Prediction slice, anomaly (task.type "anomaly") (200)** ```json { "prediction": { "task_type": "anomaly", "tier": "in_context", "threshold": 0.9, "results": [ { "row": 0, "anomaly_score": 0.97, "is_anomaly": true }, { "row": 1, "anomaly_score": 0.12, "is_anomaly": false } ] } } ``` **Large quick run, async (202)** ```json { "mode": "run", "run_id": "run_c81e0d", "status": "queued", "job_id": "job_c81e0d7f", "processing": "batch", "out": "warehouse://schema.outputs" } ``` --- # Data Source: https://docs.schemalabs.ai/api-reference/data > Register connections and datasets under /v2/data, sync snapshots, pin data for the cached rate, generate synthetic tables, and delete what you no longer need. Connections and datasets are one group under `/v2/data`. A connection is where data comes from; a dataset is an ingested or registered table, auto-classified and profiled. Each ingest or sync mints an immutable snapshot (`dsv_...`), which is what endpoints pin and held-out reports stamp. Four families of source: upload (CSV, Excel, JSON), databases (PostgreSQL, MySQL, Supabase, MongoDB, Databricks, Snowflake, Pinecone, Chroma), cloud storage (Google Drive, GCP Storage, AWS S3), and APIs (REST, GraphQL). Small files upload directly through the platform; everything else is a registered connection referenced at run or creation time. ## The data objects `GET /v2/data` returns both kinds. A dataset carries its current snapshot and pin state; a connection carries its scheme ref. Neither carries analytical outputs (sector, profile): those come from a run and live on results and reports. **Attributes** - `id` (string): `ds_...` for a dataset, `conn_...` for a connection. Immutable. - `kind` (string): Object kind. One of: `dataset`, `connection`. - `name` (string): Display name. - `source` (string): Scheme ref for connections (`snowflake://sales/live`, `s3://bucket/exports/`, `postgres://...`, `rest+https://...`); `upload` for direct uploads. - `snapshot` (string | null): Datasets: the current `dsv_...` snapshot id. - `snapshot_at` (string | null): Timestamp of the current snapshot. - `rows` (integer): Datasets: row count of the current snapshot. - `cols` (integer): Datasets: column count. - `cells` (integer): Datasets: `rows x cols`, the exact metering size of a fresh pass over it. - `size_bytes` (integer): Stored size, for the storage line. - `pinned` (boolean): Datasets: pinned datasets bill the cached rate on repeat calls indefinitely and count toward plan storage. - `pinned_by` (array of strings): Endpoint ids currently serving this dataset. A dataset pinned by a live endpoint stays pinned until that endpoint is deleted or refreshed onto other data. ```json { "id": "ds_crm01", "kind": "dataset", "name": "crm_contacts", "source": "snowflake://example/sales/public/live_accounts", "snapshot": "dsv_91f2", "snapshot_at": "2026-08-12T09:31:00Z", "rows": 22400, "cols": 12, "cells": 268800, "size_bytes": 3145728, "pinned": true, "pinned_by": [ "{endpoint_id}" ] } ``` ## Operations ### Connect a source `POST /v2/data/connect` (scope: `manage`) Registers a connection to a database, cloud bucket, or API, and ingests an initial snapshot. Credentials are stored encrypted at rest and are write-only; rotation is by re-entering them. Every connection creation and every sync is recorded in the audit log. **Body** (application/json) - `source` (string, required): Scheme ref: the provider scheme followed by the provider’s own path to the table or location, for example `snowflake://...`, `postgres://...`, `s3://bucket/prefix/`, `gdrive://folder-id`, `rest+https://host/path`. - `name` (string): Display name. Defaults to the last path segment. - `credentials` (object): Provider-specific credentials (for example `user`, `password`, `role`, `warehouse` for Snowflake; `access_key_id`, `secret_access_key` for S3). Never returned. - `options` (object): Provider options such as `query` (a SQL statement to materialize as the dataset) or `sheet`. **Returns** The connection object and the dataset it produced, with the first snapshot. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/data/connect' \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "source": "snowflake://acme/sales/public/live_accounts", "name": "sales_live", "credentials": { "user": "schema_reader", "password": "••••••••", "role": "READER", "warehouse": "WH_XS" } }' ``` **Example response (201)** ```json { "connection": { "id": "conn_3f9a", "kind": "connection", "name": "sales_live", "source": "snowflake://acme/sales/public/live_accounts" }, "dataset": { "id": "ds_sales01", "kind": "dataset", "name": "live_accounts", "source": "snowflake://acme/sales/public/live_accounts", "snapshot": "dsv_0c44", "rows": 51200, "cols": 18, "cells": 921600, "pinned": false, "pinned_by": [] } } ``` ### List data `GET /v2/data` (scope: `read`) Lists connections and datasets with their snapshot and pin state. **Query parameters** - `limit` (integer, default 20): Page size. - `cursor` (string): Cursor from a previous page. **Returns** A page of data objects and a `next_cursor`. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/data?limit=20' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "datasets": [ { "id": "ds_crm01", "kind": "dataset", "name": "crm_contacts", "source": "snowflake://example/sales/public/live_accounts", "snapshot": "dsv_91f2", "rows": 22400, "cols": 12, "cells": 268800, "pinned": true, "pinned_by": [ "{endpoint_id}" ] }, { "id": "ds_bill01", "kind": "dataset", "name": "billing_db", "source": "postgres://billing/live", "snapshot": "dsv_c4e7", "rows": 22400, "cols": 6, "cells": 134400, "pinned": true, "pinned_by": [ "{endpoint_id}" ] }, { "id": "ds_claims", "kind": "dataset", "name": "claims_2025", "source": "upload", "snapshot": "dsv_77e0", "rows": 500000, "cols": 30, "cells": 15000000, "pinned": true, "pinned_by": [ "{other_endpoint_id}" ] }, { "id": "conn_3f9a", "kind": "connection", "name": "sales_live", "source": "snowflake://acme/sales/public/live_accounts", "dataset": "ds_sales01", "snapshot": "dsv_1d09" } ], "next_cursor": null, "total": 4 } ``` ### Retrieve data `GET /v2/data/:id` (scope: `read`) Returns a dataset’s profile (rows, columns, current snapshot, pin state) or a connection’s status. **Path parameters** - `id` (string, required): `ds_...` or `conn_...`. **Returns** The data object. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/data/ds_crm01' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "ds_crm01", "kind": "dataset", "name": "crm_contacts", "source": "snowflake://example/sales/public/live_accounts", "snapshot": "dsv_91f2", "snapshot_at": "2026-08-12T09:31:00Z", "rows": 22400, "cols": 12, "cells": 268800, "size_bytes": 3145728, "pinned": true, "pinned_by": [ "{endpoint_id}" ] } ``` ### Sync a connection `POST /v2/data/:id/sync` (scope: `manage`) Re-pulls a connection and mints a new immutable snapshot (`dsv_...`) on its dataset. Endpoints pinned to the dataset keep serving the previous snapshot until you refresh them. **Path parameters** - `id` (string, required): `conn_...` or the dataset id it feeds. **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 a job for large sources, or `200` with the new snapshot for small ones. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/data/conn_3f9a/sync' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "conn_3f9a", "dataset": "ds_sales01", "snapshot": "dsv_1d09", "previous_snapshot": "dsv_0c44", "diff": { "rows_added": 310, "rows_changed": 12, "rows_removed": 4, "rows_unchanged": 51184 }, "synced_at": "2026-08-16T09:00:03Z" } ``` > A sync itself is not metered. What a subsequent pass pays follows the diff: added and changed rows bill fresh, unchanged rows bill the cached rate, removed rows bill nothing. ### Pin a dataset `POST /v2/data/:id/pin` (scope: `manage`) Pins a dataset: repeat calls over it bill the cached rate indefinitely, and it counts toward plan storage. Endpoint creation pins its datasets automatically. **Path parameters** - `id` (string, required): `ds_...`. **Returns** The dataset object with `pinned: true`. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/data/ds_sales01/pin' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "ds_sales01", "kind": "dataset", "pinned": true, "pinned_by": [] } ``` ### Unpin a dataset `POST /v2/data/:id/unpin` (scope: `manage`) Unpins a dataset. The cached rate expires shortly after last use; the next pass bills fresh. A dataset pinned by a live endpoint stays pinned (`409` names the endpoint). **Path parameters** - `id` (string, required): `ds_...`. **Returns** The dataset object with `pinned: false`, or `409 conflict` naming the dependent endpoint. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/data/ds_crm01/unpin' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "ds_crm01", "kind": "dataset", "pinned": false } ``` **Pinned by a live endpoint (409)** ```json { "error": { "type": "conflict", "message": "dataset ds_crm01 is pinned by live endpoint churn ({endpoint_id}); delete the endpoint or refresh it onto other data first", "request_id": "req_2c8f1b" } } ``` ### Generate synthetic data `POST /v2/data/generate` (scope: `manage`) Produces a synthetic tabular dataset for a sector with realistic per-column ranges. The result is an ordinary dataset (`ds_...`), useful for cold-start when representative data is thin. **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) - `sector` (string, required): Sector name, in the same vocabulary the sector output uses, for example `"hospital operations"` or `"consumer financial services"`. - `rows` (integer, required): Rows to generate. - `name` (string): Dataset display name. - `options` (object): `processing: "batch"` runs the generation as a batch job at the batch rate. **Returns** The new dataset: id, name, columns, rows, and the processing block. Large generations run as batch jobs and return `202` with a job. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/data/generate' \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Idempotency-Key: " \ -H "Content-Type: application/json" \ -d '{ "sector": "hospital operations", "rows": 5000, "name": "hospital_ops_synth" }' ``` **Example response (200)** ```json { "dataset": "ds_synth03", "name": "hospital_ops_synth.csv", "sector": "hospital operations", "rows": 5000, "columns": [ "patient_id", "age", "sex", "diagnosis_code", "lvef_pct", "admission_date" ], "processing": { "mode": "realtime" } } ``` > Metering: `C = cells generated` at the synthetic rate; the output grid is the meter. ### Delete data `DELETE /v2/data/:id` (scope: `delete`) Removes a dataset or connection immediately and irreversibly. A dataset pinned by a live endpoint cannot be deleted: the `409 conflict` names the dependency, so delete the endpoint or refresh it onto other data first. **Path parameters** - `id` (string, required): `ds_...` or `conn_...`. **Returns** The deleted id and status, or `409 conflict` naming dependent endpoints. **Example request (cURL)** ```bash curl -X DELETE 'https://api.schemalabs.ai/v2/data/ds_old01' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "ds_old01", "status": "deleted", "deleted_at": "2026-08-16T10:11:02Z" } ``` --- # Endpoints Source: https://docs.schemalabs.ai/api-reference/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: " \ -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: " ``` **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: " \ -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. --- # Reports Source: https://docs.schemalabs.ai/api-reference/reports > List and retrieve held-out reports: the immutable trust artifact minted by every endpoint create, refresh, and upgrade, with its split, baseline, and snapshot. Every endpoint create, refresh, and upgrade emits exactly one held-out report: the trust artifact. A report is immutable, names its split (method, sizes, seed), its baseline, its data snapshot, and its weakest slice, so a number never travels without provenance. Stateless runs also mint a run record in this listing (`kind: "run"`). Report display names follow `{endpoint}.{base}.{op}.{YYYY-MM-DD}` (base abbreviated `s1`, `s2`), for example `churn.s2.upgrade.2026-08-14`. The stable reference is the `r_...` id. Reports are kept for the life of the organization; deleting an endpoint does not delete its reports. ## The report object `score` with its `metric` name, then metric fields by task type: classification carries `auc` and `per_class`; regression carries `rmse`, `nrmse`, `r2`, and `quantile_calibration`; anomaly carries `precision`, `recall`, `pr_auc` where labels exist, or `score_separation` flagged unsupervised. Every report states a baseline. **Attributes** - `id` (string): `r_...`, immutable. - `name` (string): Display name at mint time, `{endpoint}.{base}.{op}.{date}`. A later rename of the endpoint does not change past names. - `endpoint` (string): Endpoint name at mint time. - `endpoint_id` (string): The endpoint ID. - `kind` (string): The operation that minted the report; `run` marks a stateless run record. One of: `create`, `refresh`, `upgrade`, `run`. - `base` (string): Base evaluated. - `pinned` (boolean): Starred reports float to the top of history. Pinning affects prominence, not retention. Set by starring on the platform’s Reports page. - `target` (object): `column`, `task`, and `classes` for classification. - `data` (object): `snapshot` (`dsv_...`), `version` timestamp, `rows`. The grade is tied to this snapshot and can expire on drift. - `split` (object): `method` (`in_context_holdout`), `train_rows`, `test_rows`, `seed`, `stratified`, and `unit` (`entity` when the split is by matched entity). - `metrics` (object): Task-appropriate metrics plus `baseline_majority` (or `baseline_mean`) and `lift_pp`. - `confidence` (object): `calibrated` (confidences on Schema models are calibrated probabilities) and a `spread` note. - `weakest_slice` (string): The slice with the lowest score, surfaced on purpose. - `previous` (object | null): On upgrade reports: `{ report_id, base, score }` and `delta`. ```json { "id": "r_8f3a", "name": "churn.s1.create.2026-03-10", "endpoint": "churn", "endpoint_id": "{endpoint_id}", "kind": "create", "base": "schema-1", "created_at": "2026-03-10T14:09:31Z", "pinned": true, "target": { "column": "crm_contacts.c3", "task": "classification", "classes": [ "retained", "churned" ] }, "data": { "snapshot": "dsv_2b81", "version": "2026-03-10 14:02", "rows": 22094 }, "split": { "method": "in_context_holdout", "train_rows": 17675, "test_rows": 4419, "seed": 42, "stratified": true, "unit": "entity" }, "metrics": { "score": 0.942, "metric": "accuracy", "auc": 0.961, "baseline_majority": 0.71, "lift_pp": 23.2, "per_class": { "retained": 0.96, "churned": 0.88 } }, "confidence": { "calibrated": true, "spread": "most predictions high-confidence" }, "weakest_slice": "tenure<3mo: 0.79", "cross_table": { "column_alignment": [ { "a": "crm_contacts.c1", "b": "billing_db.c1", "attribute": "phone", "confidence": 0.96 } ] }, "estimate_note": "held-out is a creation-time estimate on unseen rows from the same snapshot; the live endpoint uses the full pinned data." } ``` ## Operations ### List reports `GET /v2/reports` (scope: `read`) Lists held-out reports, newest first, pinned first. **Query parameters** - `limit` (integer, default 20): Page size. - `cursor` (string): Cursor from a previous page. **Returns** A page of report summaries. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/reports?limit=3' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "reports": [ { "id": "r_a7d0", "name": "churn.s2.upgrade.2026-08-14", "kind": "upgrade", "base": "schema-2", "metric": "accuracy", "score": 0.961, "delta": "+0.013", "pinned": false, "created_at": "2026-08-14T11:20:05Z" }, { "id": "r_b5e1", "name": "churn.s1.refresh.2026-08-12", "kind": "refresh", "base": "schema-1", "metric": "accuracy", "score": 0.948, "pinned": false, "created_at": "2026-08-12T09:31:44Z" }, { "id": "r_8f3a", "name": "churn.s1.create.2026-03-10", "kind": "create", "base": "schema-1", "metric": "accuracy", "score": 0.942, "pinned": true, "created_at": "2026-03-10T14:09:31Z" } ], "next_cursor": null, "total": 3 } ``` ### Retrieve a report `GET /v2/reports/:id` (scope: `read`) Returns the full held-out report. **Path parameters** - `id` (string, required): `r_...`. **Returns** The [report object](#the-report-object). **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/reports/r_8f3a' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "r_8f3a", "name": "churn.s1.create.2026-03-10", "endpoint": "churn", "endpoint_id": "{endpoint_id}", "kind": "create", "base": "schema-1", "created_at": "2026-03-10T14:09:31Z", "pinned": true, "target": { "column": "crm_contacts.c3", "task": "classification", "classes": [ "retained", "churned" ] }, "data": { "snapshot": "dsv_2b81", "version": "2026-03-10 14:02", "rows": 22094 }, "split": { "method": "in_context_holdout", "train_rows": 17675, "test_rows": 4419, "seed": 42, "stratified": true, "unit": "entity" }, "metrics": { "score": 0.942, "metric": "accuracy", "auc": 0.961, "baseline_majority": 0.71, "lift_pp": 23.2, "per_class": { "retained": 0.96, "churned": 0.88 } }, "confidence": { "calibrated": true, "spread": "most predictions high-confidence" }, "weakest_slice": "tenure<3mo: 0.79", "cross_table": { "column_alignment": [ { "a": "crm_contacts.c1", "b": "billing_db.c1", "attribute": "phone", "confidence": 0.96 } ] }, "estimate_note": "held-out is a creation-time estimate on unseen rows from the same snapshot; the live endpoint uses the full pinned data." } ``` --- # Jobs Source: https://docs.schemalabs.ai/api-reference/jobs > List, poll, and cancel the async jobs behind endpoint creation, refresh, upgrade, synthetic data generation, and batch quick runs. Endpoint creation, refresh, upgrade, synthetic generation, and large or batch runs are async so clients and CI never block. Each returns `202` with a `job_id`. Poll the job for status; job completion also raises a platform notification and an email. A job either completes with its full result or fails as `job_failed`. Failed jobs bill nothing. A batch job that misses its completion window expires unbilled and can be resubmitted. ## The job object **Attributes** - `job_id` (string): `job_...`, immutable. - `kind` (string): What the job does. `batch` is a run or generation submitted with `processing: "batch"`. One of: `run`, `create`, `refresh`, `upgrade`, `generate`, `batch`. - `status` (string): Lifecycle. Poll until terminal (`done`, `failed`, `cancelled`, `expired`). One of: `queued`, `running`, `done`, `failed`, `cancelled`, `expired`. - `progress` (integer): 0 to 100. - `phase` (string | null): Current phase label while running. - `label` (string): Human label: the endpoint name, run report id, or job description. - `endpoint_id` (string): The endpoint the job acts on; empty for stateless runs. - `report_id` (string): The report the job minted, once complete. - `dataset_id` (string): On a completed `generate` job, the id of the dataset it produced (`ds_…`). Empty for other kinds. - `eta` (object | null): `basis`, `due_at`, `seconds_remaining`. `null` once the job runs. - `error` (string): On failure: the error message. Empty otherwise. - `logs` (array of strings): Recent operational log lines (retrieve only). - `created_at` (string): Submit time. - `updated_at` (string): Last state change. ```json { "job_id": "job_9a5b01d4", "kind": "create", "status": "running", "progress": 60, "phase": "held-out evaluation", "label": "churn", "endpoint_id": "{endpoint_id}", "report_id": "", "eta": null, "error": "", "logs": [], "created_at": "2026-03-10T14:07:02Z", "updated_at": "2026-03-10T14:08:41Z" } ``` ## Operations ### List jobs `GET /v2/jobs` (scope: `read`) Lists in-flight and recent jobs. **Query parameters** - `status` (string): Filter by status. - `limit` (integer, default 20): Page size. - `cursor` (string): Cursor from a previous page. **Returns** A page of job objects. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/jobs?status=queued' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "jobs": [ { "job_id": "job_4c1f88a2", "kind": "batch", "status": "queued", "progress": 0, "phase": "", "label": "stateless run", "endpoint_id": "", "report_id": "", "eta": { "basis": "queued", "due_at": "2026-08-15T14:07:02Z", "seconds_remaining": null }, "error": "", "created_at": "2026-08-14T14:07:02Z", "updated_at": "2026-08-14T14:07:02Z" } ], "next_cursor": null, "total": 1 } ``` ### Retrieve a job `GET /v2/jobs/:id` (scope: `read`) Returns a job’s status, progress, ETA, and logs. Poll this until `status` is terminal. **Path parameters** - `id` (string, required): `job_...`. **Returns** The [job object](#the-job-object). **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/jobs/job_9a5b01d4' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "job_id": "job_9a5b01d4", "kind": "create", "status": "done", "progress": 100, "phase": null, "label": "churn", "endpoint_id": "{endpoint_id}", "report_id": "r_8f3a", "eta": null, "error": "", "logs": [ "14:07:02 pinned ds_crm01 @ dsv_2b81, ds_bill01 @ dsv_c4e7", "14:08:41 held-out: in-context split, seed 42, 17675/4419", "14:09:31 live at /v2/serve/{endpoint_id}" ] } ``` > Poll with backoff (for example 2s, 4s, 8s, then every 15s). Creation typically completes in minutes; batch jobs run within a completion window, shown on the job’s eta. ### Cancel a job `POST /v2/jobs/:id/cancel` (scope: `manage`) Stops a queued or running job. Cancelled jobs bill nothing. **Path parameters** - `id` (string, required): `job_...`. **Returns** The job object with `status: "cancelled"`. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/jobs/job_b3d1f0e2/cancel' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "job_id": "job_b3d1f0e2", "kind": "batch", "status": "cancelled", "updated_at": "2026-08-14T14:08:10Z" } ``` > Cancelling an endpoint creation stops the creation; delete the endpoint or create it again. Cancelling a refresh or upgrade leaves the endpoint serving its previous state. --- # Models Source: https://docs.schemalabs.ai/api-reference/models > List the Schema models available to your organization, which one is the default, and any Enterprise fine-tune you own. Schema models: the bases you can run on and create endpoints on. The base flagged `default: true` is used when `base` is omitted; a retired base rejects runs, creations, and serves with `422 capability_unavailable` (upgrade endpoints still on one). Enterprise fine-tunes appear in this list for the owning organization only, and are served behind ordinary endpoints. One rate for every Schema model: every base bills at the same rate, and switching an endpoint to a newer base (`upgrade`) never changes its rate. ## Operations ### List models `GET /v2/models` (scope: `read`) Returns the Schema models available to your organization. **Returns** A list of model objects, latest first. **Response fields** - `models` (array of objects): Model objects. - `id` (string): Base name (`schema-{n}`) or an Enterprise fine-tune id. - `name` (string): Display name. - `status` (string): `available`. Listed bases accept runs, creations, and serves; a base that has been retired answers with `422 capability_unavailable` and is moved on with `POST /v2/endpoints/:id/upgrade`. - `default` (boolean): The base used when a request omits `base`. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/models' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "models": [ { "id": "schema-2", "name": "Schema-2", "status": "available", "default": true } ], "request_id": "req_a4c07b" } ``` --- # Usage Source: https://docs.schemalabs.ai/api-reference/usage > Read your organization's usage meter: cells drawn per surface and per operation, percent of the weekly allowance used, and add-usage settings. One organization, one meter. Usage from the platform and from the API draws the same weekly allowance and the same bill; the platform Usage page renders this exact route. The unit is the cell (one row x one column); every operation draws the allowance in proportion to its rate (see [Usage and billing](https://docs.schemalabs.ai/billing)). Consumption is reported in absolute cells and the allowance as percent used with its reset time. Usage alerts fire at 80% and 100% of the weekly allowance as platform notifications and email. ## Operations ### Retrieve usage `GET /v2/usage` (scope: `read`) Returns the organization’s usage for the current allowance period, broken down by surface and by operation. **Returns** The usage object. **Response fields** - `org` (string): Organization name. - `plan` (string): Current plan; `api` for pure pay-as-you-go with no subscription. One of: `free`, `plus`, `pro`, `enterprise`, `api`. - `window` (string): The current allowance period, `start -> end`. Weekly, no rollover. - `resets_in` (string): Time until the weekly reset, for example `3d` or `4h`. - `days` (integer): Days in the window. - `used_pct` (number): Percent of the period’s allowance drawn, in fresh-cell equivalents. Your allowance is set by your plan. - `total_cells` (integer): Absolute fresh-cell equivalents drawn this period. - `by_surface` (object): Draw per surface (`api`, `ui`), in fresh-cell equivalents. - `by_op` (object): Draw per operation (`run`, `cached`, `create`, `refresh`, `serve`, `batch`, `generate`), in fresh-cell equivalents. - `by_op_multiplier` (object): The ratio each operation draws at, relative to the fresh rate, following the published rate card. See Usage and billing. - `by_op_raw` (object): Cells per operation as counted, before the rate ratio is applied. - `top_endpoints` (array of objects): Endpoints by draw: `endpoint`, `id`, `cells`. - `add_usage` (object): `enabled`, `spend_cap_set`, `spend_cap_usd`, `spend_cap_managed_in` (the spend cap is set on the platform under Billing). - `events` (array of objects): Per-request usage events: `day`, `op`, `cells`, `tables`, `model`, `key`, `endpoint_id`, `requests`, `surface`. - `lifetime` (boolean): `true` on Free, where the allotment is lifetime and does not reset. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/usage' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "org": "org_4f2a9c", "plan": "pro", "window": "2026-08-12 -> 2026-08-19", "resets_in": "3d", "days": 7, "used_pct": 39, "total_cells": 54400000, "by_surface": { "api": 49400000, "ui": 5000000 }, "by_op": { "run": 21200000, "cached": 13200000, "create": 15000000, "generate": 5000000 }, "by_op_multiplier": { "run": 1, "cached": 0.1, "create": 1, "generate": 0.5 }, "by_op_raw": { "run": 21200000, "cached": 132000000, "create": 15000000, "generate": 10000000 }, "top_endpoints": [ { "endpoint": "churn", "id": "{endpoint_id}", "cells": 16400000 } ], "add_usage": { "enabled": true, "spend_cap_set": false, "spend_cap_usd": 0 }, "events": [ { "day": "2026-08-15", "op": "run", "cells": 45000, "tables": 2, "model": "schema-2", "key": "key_77ab", "endpoint_id": "", "requests": 1, "surface": "api" } ], "lifetime": false } ``` --- # API keys Source: https://docs.schemalabs.ai/api-reference/keys > List, create, rotate, and revoke org-scoped API keys carrying the read, run, serve, manage, and delete scopes. API keys are org-scoped credentials with an operation scope. Outputs are bundled, so keys are never scoped per output. The secret (`sk_live_...`) is shown once at creation and never again; the key id (`key_...`) is what listings show. Scopes: `read` (list and get endpoints, data, reports, jobs, usage, models, keys), `run` (stateless inference on the bases), `serve` (call live endpoints), `manage` (create endpoints, refresh, upgrade, data connect, sync, pin, generate, cancel jobs, manage keys), `delete` (remove endpoints and data, revoke keys). Reports are managed on the platform’s Reports page. ## The key object **Attributes** - `id` (string): `key_...`, immutable. - `name` (string): Display name, for example `pipeline-prod`. - `key_preview` (string): First and last characters of the secret for recognition, for example `sk_liv...a1b2`. - `scopes` (array of strings): Any of `read`, `run`, `serve`, `manage`, `delete`. - `rate_limit` (integer): The key’s request ceiling in requests per minute; the value reflects your plan. - `requests` (integer): Requests made with this key. - `created_at` (string): Creation time. - `last_used` (string | null): Last request time. - `key` (string): The full secret. Present only in the create and rotate responses, shown once. ```json { "id": "key_77ab", "name": "pipeline-prod", "key_preview": "sk_liv...a1b2", "rate_limit": 600, "requests": 1042, "scopes": [ "serve" ], "created_at": "2026-08-01T10:00:00Z", "last_used": "2026-08-16T09:12:04Z" } ``` ## Operations ### List keys `GET /v2/keys` (scope: `read`) Lists the organization’s API keys, without secrets. **Returns** A list of key objects. **Example request (cURL)** ```bash curl 'https://api.schemalabs.ai/v2/keys' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "keys": [ { "id": "key_77ab", "name": "pipeline-prod", "key_preview": "sk_liv...a1b2", "rate_limit": 600, "requests": 1042, "scopes": [ "serve" ], "created_at": "2026-08-01T10:00:00Z", "last_used": "2026-08-16T09:12:04Z" }, { "id": "key_10c2", "name": "ci", "key_preview": "sk_liv...2c8e", "rate_limit": 600, "requests": 88, "scopes": [ "read", "run", "manage" ], "created_at": "2026-07-20T15:32:00Z", "last_used": "2026-08-15T22:01:40Z" } ], "request_id": "req_8f2c1a" } ``` ### Create a key `POST /v2/keys` (scope: `manage`) Creates an API key. The secret is returned once, in this response only. **Body** (application/json) - `name` (string, required): Display name. - `scopes` (array of strings, required): One or more of `read`, `run`, `serve`, `manage`, `delete`. **Returns** The key object including `key`, the full secret, shown once. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/keys' \ -H "Authorization: Bearer $SCHEMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "pipeline-prod", "scopes": ["serve"] }' ``` **Example response (201)** ```json { "id": "key_77ab", "name": "pipeline-prod", "key_preview": "sk_liv...a1b2", "rate_limit": 600, "requests": 0, "scopes": [ "serve" ], "created_at": "2026-08-01T10:00:00Z", "last_used": null, "key": "" } ``` > Key and billing management also live on the platform’s API page. Store the secret in a secrets manager; it cannot be retrieved again. ### Rotate a key `POST /v2/keys/:id/rotate` (scope: `manage`) Issues a new secret for the key and invalidates the old one immediately. Same id, same scopes. **Path parameters** - `id` (string, required): `key_...`. **Returns** The key object with the new secret in `key`, shown once. The old secret stops working immediately. **Example request (cURL)** ```bash curl -X POST 'https://api.schemalabs.ai/v2/keys/key_77ab/rotate' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "id": "key_77ab", "name": "pipeline-prod", "key_preview": "sk_liv...9f4d", "rate_limit": 600, "requests": 0, "scopes": [ "serve" ], "created_at": "2026-08-01T10:00:00Z", "last_used": null, "key": "" } ``` ### Revoke a key `DELETE /v2/keys/:id` (scope: `delete`) Revokes a key immediately. The key stops working and leaves the listing. **Path parameters** - `id` (string, required): `key_...`. **Returns** A deletion confirmation. **Example request (cURL)** ```bash curl -X DELETE 'https://api.schemalabs.ai/v2/keys/key_10c2' \ -H "Authorization: Bearer $SCHEMA_API_KEY" ``` **Example response (200)** ```json { "deleted": true, "id": "key_10c2" } ```