UiPath Documentation
industry-department-solutions
latest
false
  • Overview
    • Introduction
    • Getting Started
    • Integration walkthrough
    • API Guide
    • Schema lifecycle
    • Scheduled ingestion
    • Historical data ingestion
    • Data Quality Dashboard
    • Customizations
    • Data Onboarding Checklist
  • API Resources

Supply Chain & Retail Solutions API guide

Schema lifecycle

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.

Lifecycle overview

A schema goes through three stages:

  1. Save the standard schema for an application (one-time per appName + appVersion).
  2. Roll out that standard schema for a specific tenant solution. This creates the warehouse tables (and a matching <table_name>_failed_rows table for each) and records a tenant-scoped schema entry.
  3. 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

Payload

FieldRequiredDescription
appNameYesApplication identifier the schema belongs to (e.g., quote-pricing)
appVersionYesSemantic version of the schema (e.g., 1.0.0)
schemaDefinitionYesSchema specification as a JSON string. See Schema definition structure below for what goes inside it.
schemaSynergyTagNoOptional tag used by downstream metric publishing
dryRunNoWhen true, validates the payload without persisting. Returns 200 OK instead of 201 Created. Default false.

Schema definition structure

schemaDefinition is a JSON-encoded array of table definitions. Each table has columns, and each column declares its data type and a list of validations.

Per-table fields:

FieldRequiredDescription
tableNameYesTable name. Must match ^[A-Za-z_][A-Za-z0-9_]*$.
columnsYesNon-empty array of column definitions (see below).
primaryKeysNoArray of column names that form the primary key.
uniqueKeysNoArray of arrays; each inner array is a set of column names that must be unique together.
foreignKeysNoArray of foreign-key definitions, each with referencedTable, columnMappings, and an optional constraintName.
skipValidationsNoWhen true, runtime validation is skipped for this table. Default false.
scheduleConfigNoPer-table scheduled-ingestion configuration. See Scheduled ingestion.

Per-column fields:

FieldRequiredDescription
columnNameYesColumn name. Must match ^[A-Za-z_][A-Za-z0-9_]*$.
dataTypeYesOne of boolean, date, float, integer, json, numeric, string, timestamp. See Data Types.
validationsYesNon-empty array of validation rules — at least one entry per column (see below).
validations[].typeYesThe rule for each entry. One of required, optional, nonNull, minLength, maxLength, range, enum, timestampFormat. Every column must include exactly one of required or optional.
precisionConditionalRequired when dataType is numeric (1–38). Rejected when dataType is float. Ignored for other types.
scaleConditionalRequired when dataType is numeric (0–37). Rejected when dataType is float. Ignored for other types.
defaultValueNoDefault value applied when the column is added to existing rows via schema upgrade or add-column.

Validation rules — what to include in validations[]:

Every entry in a column's validations array is a { "type": "<rule>", ... } object. The validations array is required and must not be empty: every column has to state its required-ness explicitly.

Required vs optional columns. Every column must declare exactly one of these two markers:

  • { "type": "required" } — the column's key must be present in every ingest payload. Submitting a row without it fails with DI_E_23N01.
  • { "type": "optional" } — callers can leave the key out, and the column is stored as NULL — or its defaultValue, if one is set.

A column that declares neither marker, declares both, or supplies an empty validations array is rejected at save-schema with 400 Bad Request. Two further constraints apply:

  • Primary-key columns must be required and cannot be optional. Declaring a primary-key column as optional (or without required) returns 400 Bad Request.
  • nonNull may only be used together with required — a nonNull + optional column is rejected with 400 Bad Request. To make a column optional but never stored as null, set a defaultValue instead of using nonNull.

One rule is always enforced regardless of required-ness: every date or timestamp column must include { "type": "timestampFormat", "format": "<format>" }, where format is one of:

  • YYYY-MM-DD — for date
  • YYYY-MM-DD HH:MI:SS — for timestamp (no timezone)
  • YYYY-MM-DD HH:MI:SS ±hh:mm — for timestamp (with timezone)

All other validation types are optional — include them only where you want their effect. To turn one off, just leave it out of the validations array; there is no per-validator enabled: false flag.

TypeParametersEffect when included
nonNullnoneValue cannot be null or blank. (required controls key presence; nonNull controls the value.)
minLengthvalue (integer)Minimum string length.
maxLengthvalue (integer)Maximum string length.
rangemin, max (numbers)Numeric value must fall within the inclusive range.
enumvalue (array)Value must be one of the listed options.

Required-ness and nullability

A column's required-ness is set by its required/optional marker, and nonNull controls whether the value may be null: required/optional control whether the key must be present in the payload, nonNull controls the value. Choose the combination that matches how the column should behave:

You wantMarkerInclude nonNull?
Key must be present, value cannot be nullrequiredYes
Key must be present, value can be nullrequiredNo
Key may be omitted; stored as null (or defaultValue) when absentoptionalNo

The nonNull-with-optional (or nonNull without required) combination is rejected — to make a column optional but never stored as null, give it a defaultValue instead of nonNull. Primary-key columns must always be required.

The per-table pages in the API Guide reflect these flags as the Required and Nullable columns of each attribute table.

You can also add a column after rollout through the add-column endpoint — useful when a new optional column is needed on an already-rolled-out table; existing rows are backfilled with the supplied defaultValue.

Example column definitions:

"columns": [
  { "columnName": "order_id", "dataType": "string",
    "validations": [{ "type": "required" }, { "type": "nonNull" }] },
  { "columnName": "amount", "dataType": "numeric", "precision": 10, "scale": 2,
    "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "range", "min": 0, "max": 1000000 }] },
  { "columnName": "placed_at", "dataType": "timestamp",
    "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] },
  { "columnName": "notes", "dataType": "string",
    "validations": [{ "type": "required" }] },
  { "columnName": "customer_ref", "dataType": "string",
    "validations": [{ "type": "optional" }] },
  { "columnName": "region", "dataType": "string", "defaultValue": "UNKNOWN",
    "validations": [{ "type": "optional" }] }
]
"columns": [
  { "columnName": "order_id", "dataType": "string",
    "validations": [{ "type": "required" }, { "type": "nonNull" }] },
  { "columnName": "amount", "dataType": "numeric", "precision": 10, "scale": 2,
    "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "range", "min": 0, "max": 1000000 }] },
  { "columnName": "placed_at", "dataType": "timestamp",
    "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] },
  { "columnName": "notes", "dataType": "string",
    "validations": [{ "type": "required" }] },
  { "columnName": "customer_ref", "dataType": "string",
    "validations": [{ "type": "optional" }] },
  { "columnName": "region", "dataType": "string", "defaultValue": "UNKNOWN",
    "validations": [{ "type": "optional" }] }
]

Here order_id, amount, and placed_at are required and must carry a non-null value; notes is required (the key must be present) but its value can be null; customer_ref is optional — callers can omit it and it's stored as null; and region is optional too, but is stored as "UNKNOWN" rather than null when omitted, thanks to its defaultValue.

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 saved
  • 200 OKdryRun: true validation succeeded
  • 400 Bad Request — invalid schemaDefinition
  • 409 Conflict — a schema with this appName + appVersion already exists
  • 500 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

Payload

FieldRequiredDescription
solutionNameYesTenant-specific solution identifier (e.g., QP_OOTB)
targetSchemaNameYesWarehouse schema where tables will be created (e.g., STAGE)
appNameYesThe standard schema's appName
appVersionYesThe standard schema's appVersion
prefixNoString prepended to every table name during creation
suffixNoString 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 created
  • 207 Multi-Status — some tables succeeded, some failed; inspect successfulObjects and failedObjects
  • 400 Bad Request — request validation failed (e.g., unknown appName/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:

ColumnDescription
PEAKAUDITREQUESTIDIdentifier of the request that ingested the row
PEAKAUDITCREATEDATTimestamp when the row was first inserted
PEAKAUDITUPDATEDATTimestamp when the row was last updated (changes on every UPSERT)
PEAKAUDITREQUESTTIMETimestamp the caller's request was received
PEAKAUDITCREATEDBYPlatform user identifier from the x-auth-platformuserid header
SYSPRIMARYKEYSynthetic 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:

ColumnDescription
LOADTYPE"upsert" or "append", derived from the operationType of the request that produced the failed row
ERRORCODESJSON-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.

Note:

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

Payload

FieldRequiredDescription
solutionNameYesThe tenant solution to upgrade
appNameYesApplication identifier (must match the one used at rollout)
previousAppVersionYesThe version currently rolled out
newAppVersionYesThe 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 failed
  • 400 Bad Request — diff includes a removal; the response's validationErrors[] lists the removals detected
  • 404 Not FoundsolutionName, appName, or one of the versions does not exist
  • 500 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

Path parameters

  • objectName — The fully qualified warehouse table name as returned by Describe a solution's schema. For a standard rolled-out table that includes any solution prefix/suffix (for example QP_PRODUCTS_OOTB); for a custom table it includes the automatic C_ (Snowflake) or c_ (Redshift) table prefix as well (for example C_sales_orders_v1). The lookup is case-insensitive.

Automatic column-name prefix on standard tables

When you add a column to a table created through a standard schema rollout, the API automatically prefixes the column name in the warehouse — C_ on Snowflake (uppercase), c_ on Redshift (lowercase) — to distinguish post-rollout additions from the original schema columns. The customer sends the raw name in columnName; the prefixed name is what gets created in the warehouse, and what the response returns.

You send columnNameTenant warehouseWarehouse column createdResponse columnName
discount_pctSnowflakeC_discount_pct"C_discount_pct"
discount_pctRedshiftc_discount_pct"c_discount_pct"

Queries against the warehouse table must reference the prefixed name (SELECT C_discount_pct FROM ...).

Exception: columns added to a custom table are not prefixed — they keep the raw name you sent. The intent: the table's own C_/c_ prefix already signals it's a custom table, so individual columns inside don't need an additional marker.

Payload

FieldRequiredDescription
solutionNameYesThe tenant solution the table belongs to
columnNameYesRaw column name. The warehouse column is created with a C_/c_ prefix on standard tables (see above); custom-table columns are not prefixed.
dataTypeYesOne of the supported data typesboolean, date, float, integer, json, numeric, string, timestamp.
formatConditionalFormat string for date/timestamp types. Use YYYY-MM-DD for date; YYYY-MM-DD HH:MI:SS or YYYY-MM-DD HH:MI:SS ±hh:mm for timestamp.
precisionConditionalTotal digits (1–38). Required for numeric; rejected for float.
scaleConditionalDecimal digits (0–37). Required for numeric; rejected for float.
defaultValueNoDefault value applied to existing rows after the column is added.
validationsYesNon-empty array of validation rules. Each entry is { "type": "<rule>", ... } (for example {"type": "nonNull"}, {"type": "maxLength", "value": 100}). As with save-schema, must declare exactly one of {"type": "required"} or {"type": "optional"}.

Controlling required-ness and nullability on the new column

The required/optional marker controls the key, and nonNull controls the value — pick the combination that matches how the column should behave:

You wantMarkerInclude nonNull?
Key must be present, value cannot be nullrequiredYes
Key must be present, value can be nullrequiredNo
Key may be missing; stored as null (or defaultValue) when absentoptionalNo

As at save-schema, a column must declare exactly one of required/optional, and nonNull requires required (a nonNull + optional column is rejected with 400 Bad Request). The doc pages on the per-table guide reflect the marker and nonNull as the Required and Nullable columns of the attributes table.

defaultValue + nonNull are mutually exclusive. If you supply a defaultValue, the API rejects a payload that also lists {"type": "nonNull"} in validations with 400 Bad Request: "Cannot use 'nonNull' validation with defaultValue. defaultValue ensures the field always has a value." — pick one of the two, not both.

Example request

curl -X POST \
  'https://ingestion.peak.ai/api/v2/schema/QP_PRODUCTS_OOTB/add-attribute' \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "solutionName": "QP_OOTB",
    "columnName": "discount_pct",
    "dataType": "numeric",
    "precision": 5,
    "scale": 2,
    "defaultValue": 0,
    "validations": [
      { "type": "optional" },
      { "type": "range", "min": 0, "max": 100 }
    ]
  }'
curl -X POST \
  'https://ingestion.peak.ai/api/v2/schema/QP_PRODUCTS_OOTB/add-attribute' \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "solutionName": "QP_OOTB",
    "columnName": "discount_pct",
    "dataType": "numeric",
    "precision": 5,
    "scale": 2,
    "defaultValue": 0,
    "validations": [
      { "type": "optional" },
      { "type": "range", "min": 0, "max": 100 }
    ]
  }'

Response (200 OK)

{
  "requestId": "7be5d475-4494-4c73-a012-2c27c6da5f38",
  "message": "Column added successfully",
  "columnName": "C_discount_pct",
  "tableName": "QP_PRODUCTS_OOTB",
  "schemaVersion": 2
}
{
  "requestId": "7be5d475-4494-4c73-a012-2c27c6da5f38",
  "message": "Column added successfully",
  "columnName": "C_discount_pct",
  "tableName": "QP_PRODUCTS_OOTB",
  "schemaVersion": 2
}

The columnName in the response is the prefixed warehouse column name (C_discount_pct on Snowflake, c_discount_pct on Redshift) — that's the name you'll use when querying the warehouse. For a custom table, this would be the raw name you sent.

Status codes

  • 200 OK — column added
  • 400 Bad Request — invalid validations, unsupported dataType, or column already exists
  • 500 Internal Server Error — unexpected failure

Create a custom table

Adds a custom table to an existing tenant solution. Use this when a tenant needs a table beyond what the application's standard schema provides — for example, a tenant-specific staging table or a derived table that isn't part of the canonical schema. The columns are defined inline in the request payload.

The solution must already exist — create it first via Roll out a schema to a tenant solution. The new table inherits the solution's existing targetSchemaName automatically; you do not specify it on the request. Calling this endpoint with a solutionName that does not exist returns 404 Not Found.

POST https://ingestion.peak.ai/api/v2/schema/custom-table
POST https://ingestion.peak.ai/api/v2/schema/custom-table

Automatic name prefixing: the created warehouse table name is prefixed to distinguish it from standard-rollout tables — C_ on Snowflake (uppercase), c_ on Redshift (lowercase). The prefix is added to the front of the final table name, including any partner-supplied prefix/suffix. So tableName: "orders" with prefix: "sales_" and suffix: "_v1" becomes C_sales_orders_v1 on Snowflake. Column names are not prefixed.

Payload:

FieldRequiredDescription
solutionNameYesThe existing tenant solution to attach the new table to (e.g., QP_OOTB). Must already be rolled out.
tableNameYesBase name for the new table. See the prefixing note above for how the final warehouse name is composed.
columnsYesNon-empty list of column definitions. Each entry follows the same shape used in the standard schema (columnName, dataType, optional format, precision, scale, defaultValue, and a validations array). As at save-schema, every column's validations must declare exactly one of { "type": "required" } or { "type": "optional" }, and primary-key columns must be required.
primaryKeysNoArray of column names that form the primary key
uniqueKeysNoArray of arrays — each inner array is a set of column names that must be unique together
prefixNoAdditional string prepended to the table name (inside the automatic C_/c_ prefix)
suffixNoString appended to the table name

Example request:

curl -X POST \
  'https://ingestion.peak.ai/api/v2/schema/custom-table' \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "solutionName": "QP_OOTB",
    "tableName": "orders",
    "prefix": "sales_",
    "suffix": "_v1",
    "columns": [
      { "columnName": "ORDER_ID", "dataType": "string", "validations": [{ "type": "required" }] },
      { "columnName": "AMOUNT", "dataType": "numeric", "precision": 10, "scale": 2, "validations": [{ "type": "required" }] },
      { "columnName": "PLACED_AT", "dataType": "timestamp", "validations": [{ "type": "required" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] },
      { "columnName": "NOTES", "dataType": "string", "validations": [{ "type": "optional" }] }
    ],
    "primaryKeys": ["ORDER_ID"]
  }'
curl -X POST \
  'https://ingestion.peak.ai/api/v2/schema/custom-table' \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "solutionName": "QP_OOTB",
    "tableName": "orders",
    "prefix": "sales_",
    "suffix": "_v1",
    "columns": [
      { "columnName": "ORDER_ID", "dataType": "string", "validations": [{ "type": "required" }] },
      { "columnName": "AMOUNT", "dataType": "numeric", "precision": 10, "scale": 2, "validations": [{ "type": "required" }] },
      { "columnName": "PLACED_AT", "dataType": "timestamp", "validations": [{ "type": "required" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] },
      { "columnName": "NOTES", "dataType": "string", "validations": [{ "type": "optional" }] }
    ],
    "primaryKeys": ["ORDER_ID"]
  }'

ORDER_ID, AMOUNT, and PLACED_AT are required; NOTES is optional, so callers can omit it.

Response (201 Created):

{
  "message": "Custom table created successfully",
  "tableName": "C_sales_orders_v1",
  "solutionName": "QP_OOTB",
  "schemaVersion": 3,
  "columnCount": 4
}
{
  "message": "Custom table created successfully",
  "tableName": "C_sales_orders_v1",
  "solutionName": "QP_OOTB",
  "schemaVersion": 3,
  "columnCount": 4
}

The warehouse table name returned in the response includes the automatic C_/c_ prefix. Ingest into the custom table by sending requests to POST /api/v2/objects/<returned-tableName> — the same endpoint as any other table.

Status codes:

  • 201 Created — custom table created
  • 400 Bad Request — validation failure (missing required fields, invalid column definitions, schema-name issues)
  • 401 Unauthorized
  • 404 Not Found — referenced solution or schema does not exist
  • 409 Conflict — a table with this name already exists in the solution
  • 500 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

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}

Query parameters

  • solutionName (required) — the solution to describe

Response (200 OK)

Each table in the schema array is keyed by objectName — the full warehouse table name as it exists in your warehouse, including any solution-level prefix/suffix from rollout. Tables created through Create a custom table appear here with their automatic C_ (Snowflake) or c_ (Redshift) prefix as well.

{
  "solutionName": "QP_OOTB",
  "targetSchemaName": "STAGE",
  "appName": "quote-pricing",
  "appVersion": "1.1.0",
  "prefix": "QP_",
  "suffix": "_OOTB",
  "createdAt": "2026-04-24T07:23:19.264Z",
  "schema": [
    {
      "objectName": "QP_PRODUCTS_OOTB",
      "primaryKeys": ["product_id"],
      "uniqueKeys": [],
      "foreignKeys": [],
      "columns": [
        { "columnName": "product_id",    "dataType": "string",  "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "product_name",  "dataType": "string",  "validations": [{ "type": "required" }, { "type": "maxLength", "value": 255 }] },
        { "columnName": "discount_pct",  "dataType": "numeric", "precision": 5, "scale": 2, "validations": [{ "type": "required" }, { "type": "range", "min": 0, "max": 100 }] }
      ],
      "isRolledOut": true,
      "rolloutState": "SUCCESS",
      "targetSchemaName": "STAGE"
    },
    {
      "objectName": "C_sales_orders_v1",
      "primaryKeys": ["order_id"],
      "uniqueKeys": [],
      "foreignKeys": [],
      "columns": [
        { "columnName": "order_id",  "dataType": "string",  "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "amount",    "dataType": "numeric", "precision": 10, "scale": 2, "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "placed_at", "dataType": "timestamp", "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] }
      ],
      "isRolledOut": true,
      "rolloutState": "SUCCESS",
      "targetSchemaName": "STAGE"
    }
  ]
}
{
  "solutionName": "QP_OOTB",
  "targetSchemaName": "STAGE",
  "appName": "quote-pricing",
  "appVersion": "1.1.0",
  "prefix": "QP_",
  "suffix": "_OOTB",
  "createdAt": "2026-04-24T07:23:19.264Z",
  "schema": [
    {
      "objectName": "QP_PRODUCTS_OOTB",
      "primaryKeys": ["product_id"],
      "uniqueKeys": [],
      "foreignKeys": [],
      "columns": [
        { "columnName": "product_id",    "dataType": "string",  "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "product_name",  "dataType": "string",  "validations": [{ "type": "required" }, { "type": "maxLength", "value": 255 }] },
        { "columnName": "discount_pct",  "dataType": "numeric", "precision": 5, "scale": 2, "validations": [{ "type": "required" }, { "type": "range", "min": 0, "max": 100 }] }
      ],
      "isRolledOut": true,
      "rolloutState": "SUCCESS",
      "targetSchemaName": "STAGE"
    },
    {
      "objectName": "C_sales_orders_v1",
      "primaryKeys": ["order_id"],
      "uniqueKeys": [],
      "foreignKeys": [],
      "columns": [
        { "columnName": "order_id",  "dataType": "string",  "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "amount",    "dataType": "numeric", "precision": 10, "scale": 2, "validations": [{ "type": "required" }, { "type": "nonNull" }] },
        { "columnName": "placed_at", "dataType": "timestamp", "validations": [{ "type": "required" }, { "type": "nonNull" }, { "type": "timestampFormat", "format": "YYYY-MM-DD HH:MI:SS" }] }
      ],
      "isRolledOut": true,
      "rolloutState": "SUCCESS",
      "targetSchemaName": "STAGE"
    }
  ]
}

The objectName value is what you use as the URL path on POST /api/v2/objects/{objectName} when ingesting into the table — including the C_/c_ prefix for custom tables.

Status codes

  • 200 OK — schema returned
  • 404 Not Found — no solution with that name has been rolled out for the calling tenant

Was this page helpful?

Connect

Need help? Support

Want to learn? UiPath Academy

Have questions? UiPath Forum

Stay updated