- Overview
- API Resources
Supply Chain & Retail Solutions API guide
This page covers the schema-management endpoints: saving a standard schema for an application, rolling it out for a tenant solution, upgrading to a new version, adding a new column, and inspecting what is currently rolled out. Use it together with Getting Started and the API Guide.
All schema endpoints are rooted at https://ingestion.peak.ai/api/v2/schema. Tenant-scoped calls require x-auth-tenant and x-auth-tenantId headers; the standard-schema save endpoint does not.
Lifecycle overview
A schema goes through three stages:
- Save the standard schema for an application (one-time per
appName+appVersion). - Roll out that standard schema for a specific tenant solution. This creates the warehouse tables (and a matching
<table_name>_failed_rowstable for each) and records a tenant-scoped schema entry. - Evolve the rolled-out schema over time — by upgrading to a new app version or by adding a new column to an existing table.
You can inspect what's been rolled out at any time using the list and get endpoints.
Save a standard schema
Saves an application-level standard schema that can later be rolled out to one or more tenant solutions. Each appName + appVersion combination must be unique.
POST https://ingestion.peak.ai/api/v2/schema
POST https://ingestion.peak.ai/api/v2/schema
Headers:
Authorization- Your Personal Access Token (PAT)Content-Type: application/json
Payload:
| Field | Required | Description |
|---|---|---|
appName | Yes | Application identifier the schema belongs to (e.g., quote-pricing) |
appVersion | Yes | Semantic version of the schema (e.g., 1.0.0) |
schemaDefinition | Yes | Schema specification as a JSON string. Contains the table list, columns, data types, validations, primary keys, and unique keys. |
schemaSynergyTag | No | Optional tag used by downstream metric publishing |
dryRun | No | When true, validates the payload without persisting. Returns 200 OK instead of 201 Created. Default false. |
Example request:
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"appName": "quote-pricing",
"appVersion": "1.0.0",
"schemaDefinition": "{ \"tables\": [ { \"name\": \"products\", \"columns\": [ ... ] } ] }",
"dryRun": false
}'
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"appName": "quote-pricing",
"appVersion": "1.0.0",
"schemaDefinition": "{ \"tables\": [ { \"name\": \"products\", \"columns\": [ ... ] } ] }",
"dryRun": false
}'
Response (201 Created):
{
"id": "7be5d475-4494-4c73-a012-2c27c6da5f38",
"appName": "quote-pricing",
"appVersion": "1.0.0",
"schemaSynergyTag": null,
"createdAt": "2026-04-24T07:23:19.264Z",
"updatedAt": "2026-04-24T07:23:19.264Z",
"message": "Standard schema saved successfully"
}
{
"id": "7be5d475-4494-4c73-a012-2c27c6da5f38",
"appName": "quote-pricing",
"appVersion": "1.0.0",
"schemaSynergyTag": null,
"createdAt": "2026-04-24T07:23:19.264Z",
"updatedAt": "2026-04-24T07:23:19.264Z",
"message": "Standard schema saved successfully"
}
Status codes:
201 Created— schema saved200 OK—dryRun: truevalidation succeeded400 Bad Request— invalidschemaDefinition409 Conflict— a schema with thisappName+appVersionalready exists500 Internal Server Error— unexpected failure
Roll out a schema to a tenant solution
Creates the warehouse tables for a tenant solution by referencing a previously saved standard schema. Each table is created alongside its <table_name>_failed_rows companion table — used by asynchronous validation to record failed rows.
POST https://ingestion.peak.ai/api/v2/schema/rollout
POST https://ingestion.peak.ai/api/v2/schema/rollout
Headers:
Authorization- Your Personal Access Token (PAT)Content-Type: application/jsonx-auth-tenant(required)x-auth-tenantId(required)
Payload:
| Field | Required | Description |
|---|---|---|
solutionName | Yes | Tenant-specific solution identifier (e.g., QP_OOTB) |
targetSchemaName | Yes | Warehouse schema where tables will be created (e.g., STAGE) |
appName | Yes | The standard schema's appName |
appVersion | Yes | The standard schema's appVersion |
prefix | No | String prepended to every table name during creation |
suffix | No | String appended to every table name during creation |
Example request:
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/rollout' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"appName": "quote-pricing",
"appVersion": "1.0.0",
"prefix": "QP_",
"suffix": "_OOTB"
}'
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/rollout' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"appName": "quote-pricing",
"appVersion": "1.0.0",
"prefix": "QP_",
"suffix": "_OOTB"
}'
Response (201 Created):
{
"id": "f81c9af3-65ce-4dfe-9cd5-855ebb79e617",
"solutionName": "QP_OOTB",
"schemaVersion": 1,
"targetSchemaName": "STAGE",
"prefix": "QP_",
"suffix": "_OOTB",
"createdAt": 1745484199264,
"message": "Schema rolled out successfully",
"successfulObjects": {
"QP_PRODUCTS_OOTB": "Created",
"QP_CUSTOMERS_OOTB": "Created"
},
"failedObjects": {}
}
{
"id": "f81c9af3-65ce-4dfe-9cd5-855ebb79e617",
"solutionName": "QP_OOTB",
"schemaVersion": 1,
"targetSchemaName": "STAGE",
"prefix": "QP_",
"suffix": "_OOTB",
"createdAt": 1745484199264,
"message": "Schema rolled out successfully",
"successfulObjects": {
"QP_PRODUCTS_OOTB": "Created",
"QP_CUSTOMERS_OOTB": "Created"
},
"failedObjects": {}
}
Status codes:
201 Created— every table was created207 Multi-Status— some tables succeeded, some failed; inspectsuccessfulObjectsandfailedObjects400 Bad Request— request validation failed (e.g., unknownappName/appVersion)500 Internal Server Error— every table failed, or unexpected failure
Audit columns added at rollout
Each table created by a rollout includes a fixed set of audit columns alongside the business columns from your schema. These are populated automatically by the API — you do not send them in your ingest payload, and they will not collide with your own column names.
Data table — every rolled-out table includes these 6 audit columns:
| Column | Description |
|---|---|
PEAKAUDITREQUESTID | Identifier of the request that ingested the row |
PEAKAUDITCREATEDAT | Timestamp when the row was first inserted |
PEAKAUDITUPDATEDAT | Timestamp when the row was last updated (changes on every UPSERT) |
PEAKAUDITREQUESTTIME | Timestamp the caller's request was received |
PEAKAUDITCREATEDBY | Platform user identifier from the x-auth-platformuserid header |
SYSPRIMARYKEY | Synthetic primary key used internally to deduplicate and join rows |
Failed-rows table (<table_name>_failed_rows) — the companion table created next to each data table includes the audit columns above (except PEAKAUDITUPDATEDAT, since failed rows are not updated), plus two extras specific to failure tracking:
| Column | Description |
|---|---|
LOADTYPE | "upsert" or "append", derived from the operationType of the request that produced the failed row |
ERRORCODES | JSON-encoded list of error codes the row failed against (see API Guide — Error codes) |
In practice, you don't need to query the failed-rows table directly — the Data Quality Dashboard surfaces the same data with filters and drill-downs. See Data Quality Dashboard.
Column-name casing differs by warehouse: Snowflake stores them uppercase and unquoted (PEAKAUDITREQUESTID); Redshift stores them quoted and camel-cased ("peakAuditRequestId"). They refer to the same logical column.
Upgrade a rolled-out schema to a new version
Computes the diff between two standard schema versions and applies only additive changes (new tables, new columns) to your already-rolled-out solution. Any removal — of a table, column, primary key, unique key, or foreign key — is rejected with 400 Bad Request.
POST https://ingestion.peak.ai/api/v2/schema/upgrade
POST https://ingestion.peak.ai/api/v2/schema/upgrade
Headers:
Authorization- Your Personal Access Token (PAT)Content-Type: application/jsonx-auth-tenant(required)x-auth-tenantId(required)
Payload:
| Field | Required | Description |
|---|---|---|
solutionName | Yes | The tenant solution to upgrade |
appName | Yes | Application identifier (must match the one used at rollout) |
previousAppVersion | Yes | The version currently rolled out |
newAppVersion | Yes | The version to upgrade to |
Example request:
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/upgrade' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"appName": "quote-pricing",
"previousAppVersion": "1.0.0",
"newAppVersion": "1.1.0"
}'
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/upgrade' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"appName": "quote-pricing",
"previousAppVersion": "1.0.0",
"newAppVersion": "1.1.0"
}'
Response (200 OK):
{
"solutionName": "QP_OOTB",
"appName": "quote-pricing",
"previousAppVersion": "1.0.0",
"newAppVersion": "1.1.0",
"schemaVersion": 2,
"message": "Schema upgrade completed successfully",
"successfulObjects": {
"QP_PRODUCTS_OOTB": "Added column: discount_pct",
"QP_PROMOTIONS_OOTB": "Created"
},
"failedObjects": {},
"skippedObjects": {
"QP_CUSTOMERS_OOTB": "No changes detected"
}
}
{
"solutionName": "QP_OOTB",
"appName": "quote-pricing",
"previousAppVersion": "1.0.0",
"newAppVersion": "1.1.0",
"schemaVersion": 2,
"message": "Schema upgrade completed successfully",
"successfulObjects": {
"QP_PRODUCTS_OOTB": "Added column: discount_pct",
"QP_PROMOTIONS_OOTB": "Created"
},
"failedObjects": {},
"skippedObjects": {
"QP_CUSTOMERS_OOTB": "No changes detected"
}
}
Status codes:
200 OK— every applicable change succeeded (or the diff was empty)207 Multi-Status— some changes succeeded, some failed400 Bad Request— diff includes a removal; the response'svalidationErrors[]lists the removals detected404 Not Found—solutionName,appName, or one of the versions does not exist500 Internal Server Error— every change failed, or unexpected failure
Add a new column
Adds a single column to an existing table without going through a full version upgrade. The new column is added with the supplied validations and default value.
POST https://ingestion.peak.ai/api/v2/schema/{objectName}/add-attribute
POST https://ingestion.peak.ai/api/v2/schema/{objectName}/add-attribute
Headers:
Authorization- Your Personal Access Token (PAT)Content-Type: application/jsonx-auth-tenant(required)x-auth-tenantId(required)
Path parameters:
objectName- The base object name. The actual table affected is{prefix}{objectName}{suffix}based on the solution's rollout settings.
Payload:
| Field | Required | Description |
|---|---|---|
solutionName | Yes | The tenant solution the table belongs to |
columnName | Yes | Name of the new column |
dataType | Yes | Data type — see Data Types for supported values |
format | No | Format string for date/timestamp types (e.g., yyyy-MM-dd) |
precision | No | Total digits for numeric types (1–38) |
scale | No | Decimal digits for numeric types (0–37) |
defaultValue | No | Default value for existing rows |
validations | Yes | Array of validation rules. Each entry is { "type": "<rule>", "value": <value> } (e.g., {"type": "nonNull"}, {"type": "maxLength", "value": 100}) |
Example request:
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/PRODUCTS/add-attribute' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"columnName": "discount_pct",
"dataType": "decimal",
"precision": 5,
"scale": 2,
"defaultValue": 0,
"validations": [
{ "type": "nonNull" },
{ "type": "range", "min": 0, "max": 100 }
]
}'
curl -X POST \
'https://ingestion.peak.ai/api/v2/schema/PRODUCTS/add-attribute' \
-H 'Authorization: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"solutionName": "QP_OOTB",
"columnName": "discount_pct",
"dataType": "decimal",
"precision": 5,
"scale": 2,
"defaultValue": 0,
"validations": [
{ "type": "nonNull" },
{ "type": "range", "min": 0, "max": 100 }
]
}'
Response (200 OK):
{
"requestId": "7be5d475-4494-4c73-a012-2c27c6da5f38",
"message": "Column added successfully",
"columnName": "discount_pct",
"tableName": "QP_PRODUCTS_OOTB",
"schemaVersion": 2
}
{
"requestId": "7be5d475-4494-4c73-a012-2c27c6da5f38",
"message": "Column added successfully",
"columnName": "discount_pct",
"tableName": "QP_PRODUCTS_OOTB",
"schemaVersion": 2
}
Status codes:
200 OK— column added400 Bad Request— invalid validations, unsupporteddataType, or column already exists500 Internal Server Error— unexpected failure
List solutions for the tenant
Returns every solution that has been rolled out for the calling tenant, with the latest version's metadata. Useful for populating dropdowns and checking what's available.
GET https://ingestion.peak.ai/api/v2/schema/solutions
GET https://ingestion.peak.ai/api/v2/schema/solutions
Headers:
Authorization- Your Personal Access Token (PAT)x-auth-tenant(required)x-auth-tenantId(required)
Response (200 OK):
{
"solutions": [
{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"prefix": "QP_",
"suffix": "_OOTB",
"schemaVersion": 2,
"tableCount": 5,
"createdAt": "2026-04-24T07:23:19.264Z"
},
{
"solutionName": "atest_automation_5",
"targetSchemaName": "stage",
"prefix": null,
"suffix": null,
"schemaVersion": 1,
"tableCount": 3,
"createdAt": "2026-01-20T09:06:15.891Z"
}
]
}
{
"solutions": [
{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"prefix": "QP_",
"suffix": "_OOTB",
"schemaVersion": 2,
"tableCount": 5,
"createdAt": "2026-04-24T07:23:19.264Z"
},
{
"solutionName": "atest_automation_5",
"targetSchemaName": "stage",
"prefix": null,
"suffix": null,
"schemaVersion": 1,
"tableCount": 3,
"createdAt": "2026-01-20T09:06:15.891Z"
}
]
}
The solutions array is empty if no rollouts have happened yet for the tenant.
Describe a solution's schema
Returns the latest version's full schema for a given solution, including every table and its columns.
GET https://ingestion.peak.ai/api/v2/schema?solutionName={solutionName}
GET https://ingestion.peak.ai/api/v2/schema?solutionName={solutionName}
Headers:
Authorization- Your Personal Access Token (PAT)x-auth-tenant(required)x-auth-tenantId(required)
Query parameters:
solutionName(required) — the solution to describe
Response (200 OK):
{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"appName": "quote-pricing",
"appVersion": "1.1.0",
"prefix": "QP_",
"suffix": "_OOTB",
"createdAt": "2026-04-24T07:23:19.264Z",
"schema": [
{
"tableName": "QP_PRODUCTS_OOTB",
"columns": [
{ "name": "product_id", "dataType": "string", "primaryKey": true, "validations": [{ "type": "nonNull" }] },
{ "name": "product_name", "dataType": "string", "validations": [{ "type": "maxLength", "value": 255 }] },
{ "name": "discount_pct", "dataType": "decimal", "precision": 5, "scale": 2, "validations": [{ "type": "range", "min": 0, "max": 100 }] }
]
}
]
}
{
"solutionName": "QP_OOTB",
"targetSchemaName": "STAGE",
"appName": "quote-pricing",
"appVersion": "1.1.0",
"prefix": "QP_",
"suffix": "_OOTB",
"createdAt": "2026-04-24T07:23:19.264Z",
"schema": [
{
"tableName": "QP_PRODUCTS_OOTB",
"columns": [
{ "name": "product_id", "dataType": "string", "primaryKey": true, "validations": [{ "type": "nonNull" }] },
{ "name": "product_name", "dataType": "string", "validations": [{ "type": "maxLength", "value": 255 }] },
{ "name": "discount_pct", "dataType": "decimal", "precision": 5, "scale": 2, "validations": [{ "type": "range", "min": 0, "max": 100 }] }
]
}
]
}
Status codes:
200 OK— schema returned404 Not Found— no solution with that name has been rolled out for the calling tenant