# Append Activity Staged Changes
Source: https://docs.rastro.ai/api-reference/activities/append-activity-staged-changes
/api-reference/openapi.json post /public/activities/{activity_id}/staged-changes/append
Append staged changes to an existing activity.
This supports large one-command MCP updates by chunking staged changes across
multiple requests while keeping a single activity/review URL.
# Set Activity Pending Review Public
Source: https://docs.rastro.ai/api-reference/activities/set-activity-pending-review-public
/api-reference/openapi.json post /public/activities/{activity_id}/pending-review
Move an existing activity to pending_review and return review URL.
# Create Activity With Staged Changes
Source: https://docs.rastro.ai/api-reference/catalog-activities/create-activity-with-staged-changes
/api-reference/openapi.json post /public/catalogs/{catalog_id}/activities
Create an activity with optional staged changes for a catalog.
This endpoint allows programmatic creation of review activities (e.g., from an MCP tool server).
Staged changes represent proposed modifications to catalog items that can be reviewed and applied.
# Create Custom Transform Activity
Source: https://docs.rastro.ai/api-reference/catalog-activities/create-custom-transform-activity
/api-reference/openapi.json post /public/catalogs/{catalog_id}/activities/custom-transform
Create a custom transform activity with full audit metadata.
This is the primary endpoint for MCP tool servers to create reviewable activities
with script provenance, diff summaries, and validation reports.
# List Catalog Activities
Source: https://docs.rastro.ai/api-reference/catalog-activities/list-catalog-activities
/api-reference/openapi.json get /public/catalogs/{catalog_id}/activities
List activities for a specific catalog.
Returns activities associated with the given catalog, with optional filtering.
# Save Activity As Workflow
Source: https://docs.rastro.ai/api-reference/catalog-activities/save-activity-as-workflow
/api-reference/openapi.json post /public/catalogs/{catalog_id}/activities/{activity_id}/save-workflow
Create a reusable workflow template from an activity's transform context.
# Add Catalog Field Public
Source: https://docs.rastro.ai/api-reference/catalog-schemas/add-catalog-field-public
/api-reference/openapi.json post /public/catalogs/{catalog_id}/schema/fields
Add a new field to the catalog schema (API key or user token).
Creates a new schema version with the additional field.
# Batch Update Catalog Fields Public
Source: https://docs.rastro.ai/api-reference/catalog-schemas/batch-update-catalog-fields-public
/api-reference/openapi.json put /public/catalogs/{catalog_id}/schema/fields/batch
Batch add, update, and/or remove schema fields in one atomic operation (API key or user token).
Creates a single new schema version for all changes.
# Get Catalog Schema
Source: https://docs.rastro.ai/api-reference/catalog-schemas/get-catalog-schema
/api-reference/openapi.json get /public/catalogs/{catalog_id}/schema
Get catalog schema definition.
Returns the schema for a catalog, including field definitions, types, and metadata.
# Create Catalog Snapshot
Source: https://docs.rastro.ai/api-reference/catalog-snapshots/create-catalog-snapshot
/api-reference/openapi.json post /public/catalogs/{catalog_id}/snapshots
Create a manual snapshot for later restore.
# List Catalog Snapshots
Source: https://docs.rastro.ai/api-reference/catalog-snapshots/list-catalog-snapshots
/api-reference/openapi.json get /public/catalogs/{catalog_id}/snapshots
List catalog snapshots for rollback/history flows.
# Restore Catalog Snapshot
Source: https://docs.rastro.ai/api-reference/catalog-snapshots/restore-catalog-snapshot
/api-reference/openapi.json post /public/catalogs/{catalog_id}/snapshots/{snapshot_id}/restore
Restore a catalog to a specific snapshot.
# Delete Catalog Taxonomy Public
Source: https://docs.rastro.ai/api-reference/catalog-taxonomies/delete-catalog-taxonomy-public
/api-reference/openapi.json delete /public/catalogs/{catalog_id}/taxonomy
Remove taxonomy from catalog (API key or user token).
Creates a new schema version without the taxonomy.
# Get Catalog Taxonomy
Source: https://docs.rastro.ai/api-reference/catalog-taxonomies/get-catalog-taxonomy
/api-reference/openapi.json get /public/catalogs/{catalog_id}/taxonomy
Get catalog taxonomy with computed inheritance.
Returns the taxonomy with enriched nodes that include level, path,
children, inherited_attributes, and all_attributes.
Returns null if catalog has no taxonomy configured.
# Set Catalog Taxonomy Public
Source: https://docs.rastro.ai/api-reference/catalog-taxonomies/set-catalog-taxonomy-public
/api-reference/openapi.json put /public/catalogs/{catalog_id}/taxonomy
Set or replace the catalog taxonomy (API key or user token).
Creates a new schema version with the updated taxonomy.
Request body: taxonomy object with name, hierarchy_levels, and nodes.
Returns the enriched taxonomy with computed fields (level, path, children, inherited_attributes).
# Bulk Insert Items
Source: https://docs.rastro.ai/api-reference/catalogs/bulk-insert-items
/api-reference/openapi.json post /public/catalogs/{catalog_id}/items/bulk
Bulk create catalog items.
Args:
catalog_id: The catalog ID
request: Bulk insert request with items
Returns:
Summary of the bulk operation
# Bulk Upsert Product Variants Public
Source: https://docs.rastro.ai/api-reference/catalogs/bulk-upsert-product-variants-public
/api-reference/openapi.json post /public/catalogs/{catalog_id}/products/{product_id}/items/bulk
Bulk upsert variants under a parent product item in a single call.
The product_id path parameter is the parent product catalog item database ID, not the business product_id field stored in row data.
Merges provided product-level fields into each variant and upserts using the catalog's configured unique id field (e.g., 'sku').
# Create Catalog Public
Source: https://docs.rastro.ai/api-reference/catalogs/create-catalog-public
/api-reference/openapi.json post /public/catalogs
Create a new catalog (API key or user token).
# Create Product Variant Public
Source: https://docs.rastro.ai/api-reference/catalogs/create-product-variant-public
/api-reference/openapi.json post /public/catalogs/{catalog_id}/products/{product_id}/items
Create a new variant under a parent product item (API key or user token).
The product_id path parameter is the parent product catalog item database ID, not the business product_id field stored in row data.
Accepts variant data with at least 'sku' or 'unique_identifier'.
# Delete Catalog Item
Source: https://docs.rastro.ai/api-reference/catalogs/delete-catalog-item
/api-reference/openapi.json delete /public/catalogs/{catalog_id}/items/{item_id}
Delete a catalog item.
Args:
catalog_id: The catalog ID
item_id: The item ID (database ID)
Returns:
Success message
# Delete Catalog Public
Source: https://docs.rastro.ai/api-reference/catalogs/delete-catalog-public
/api-reference/openapi.json delete /public/catalogs/{catalog_id}
Delete a catalog and all its data (API key or user token).
# Duplicate Catalog
Source: https://docs.rastro.ai/api-reference/catalogs/duplicate-catalog
/api-reference/openapi.json post /public/catalogs/{catalog_id}/duplicate
Duplicate a catalog schema and optionally copy items.
# Get Catalog
Source: https://docs.rastro.ai/api-reference/catalogs/get-catalog
/api-reference/openapi.json get /public/catalogs/{catalog_id}
Get details of a specific catalog.
Args:
catalog_id: The catalog ID
Returns:
Catalog details including schema information
# Get Catalog Item
Source: https://docs.rastro.ai/api-reference/catalogs/get-catalog-item
/api-reference/openapi.json get /public/catalogs/{catalog_id}/items/{item_id}
Get a specific catalog item by ID.
Args:
catalog_id: The catalog ID
item_id: The item ID (database ID)
Returns:
The catalog item data
# Get Catalog Md
Source: https://docs.rastro.ai/api-reference/catalogs/get-catalog-md
/api-reference/openapi.json get /public/catalogs/{catalog_id}/catalog-md
Get the catalog's markdown context (catalog_md) injected into enrichment and mapping prompts.
# Get Catalog Quality Prompt
Source: https://docs.rastro.ai/api-reference/catalogs/get-catalog-quality-prompt
/api-reference/openapi.json get /public/catalogs/{catalog_id}/quality-prompt
Get the catalog's quality prompt (used by the judge and readiness checks).
# Get Catalog Raw Item
Source: https://docs.rastro.ai/api-reference/catalogs/get-catalog-raw-item
/api-reference/openapi.json get /public/catalogs/{catalog_id}/raw-items/{item_id}
Get a single raw catalog item row for MCP/tooling use cases.
# List Catalog Items
Source: https://docs.rastro.ai/api-reference/catalogs/list-catalog-items
/api-reference/openapi.json get /public/catalogs/{catalog_id}/items
List items in a catalog with pagination, search, and filtering.
Args:
catalog_id: The catalog ID
limit: Maximum number of items to return (default: 50, max: 1000)
offset: Number of items to skip
search: Text search across all fields
sort: Sort by field and direction (field:asc or field:desc)
Returns:
Paginated list of catalog items
# List Catalog Raw Items
Source: https://docs.rastro.ai/api-reference/catalogs/list-catalog-raw-items
/api-reference/openapi.json get /public/catalogs/{catalog_id}/raw-items
List raw catalog_items rows without public product transformation.
This endpoint is intended for MCP/tooling workloads that require full-fidelity
row data (id/entity_type/parent_id/current_version/data) for local transforms
and deterministic diff pipelines.
# List Catalogs
Source: https://docs.rastro.ai/api-reference/catalogs/list-catalogs
/api-reference/openapi.json get /public/catalogs
List all catalogs accessible to the authenticated organization.
Returns catalogs owned by the organization associated with the API key or user token.
# Update Catalog Item
Source: https://docs.rastro.ai/api-reference/catalogs/update-catalog-item
/api-reference/openapi.json put /public/catalogs/{catalog_id}/items/{item_id}
Update a catalog item.
Args:
catalog_id: The catalog ID
item_id: The item ID (database ID)
data: Updated data for the item (dynamic schema)
Returns:
The updated catalog item
# Update Catalog Md
Source: https://docs.rastro.ai/api-reference/catalogs/update-catalog-md
/api-reference/openapi.json put /public/catalogs/{catalog_id}/catalog-md
Set the catalog's markdown context (catalog_md) injected into enrichment and mapping prompts.
# Update Catalog Public
Source: https://docs.rastro.ai/api-reference/catalogs/update-catalog-public
/api-reference/openapi.json put /public/catalogs/{catalog_id}
Update catalog settings (API key or user token).
Only provided fields are updated; omitted fields remain unchanged.
# Update Catalog Quality Prompt
Source: https://docs.rastro.ai/api-reference/catalogs/update-catalog-quality-prompt
/api-reference/openapi.json put /public/catalogs/{catalog_id}/quality-prompt
Set the catalog's quality prompt (used by the judge and readiness checks).
# Update Product Public
Source: https://docs.rastro.ai/api-reference/catalogs/update-product-public
/api-reference/openapi.json put /public/catalogs/{catalog_id}/products/{product_id}
Update product-level fields on a parent product item (API key or user token).
The product_id path parameter is the parent product catalog item database ID, not the business product_id field stored in row data.
Only fields with 'product_' prefix will be applied.
# Upsert Item Public
Source: https://docs.rastro.ai/api-reference/catalogs/upsert-item-public
/api-reference/openapi.json post /public/catalogs/{catalog_id}/items
Upsert a single item using the same shape as the public read API.
# Cancel Enrichment Job
Source: https://docs.rastro.ai/api-reference/enrich/cancel-enrichment-job
/api-reference/openapi.json post /public/enrich/{job_id}/cancel
Cancel a running enrichment job.
Items already processed will be kept and credits charged only for those items.
**Example:**
```
POST /api/public/enrich/{job_id}/cancel
```
# Create Catalog From Run
Source: https://docs.rastro.ai/api-reference/enrich/create-catalog-from-run
/api-reference/openapi.json post /public/enrich/{job_id}/to-catalog
Create a new catalog from an enrichment run's results.
The new catalog's schema carries field types AND descriptions from the run's
enrichment template, and is populated with the run's enriched records. Works for
upload-based (catalog-less) runs that otherwise have no catalog to export to.
# Enrich Data
Source: https://docs.rastro.ai/api-reference/enrich/enrich-data
/api-reference/openapi.json post /public/enrich
Enrich items using web research, with optional taxonomy prediction.
Accepts both `application/json` (plain JSON body) and `multipart/form-data`
(with `request_json` field, optional `items_file`, and optional
`knowledge_base_files`).
**Two modes of operation:**
1. **Direct mode**: Pass items and output_schema directly
2. **Full-run mode**: Pass `source_activity_id` from a previous run to process all stored items
**Example: Direct enrichment**
```json
{
"prompt": "Find product specifications",
"items": [{"name": "iPhone 15 Pro"}],
"output_schema": [
{"name": "price", "type": "string", "description": "Current retail price"},
{"name": "weight", "type": "string", "description": "Product weight"}
]
}
```
**Example: Dry run (process first 10 items)**
```json
{
"prompt": "Find product specifications",
"items": [... all 1000 items ...],
"output_schema": [...],
"max_rows": 10
}
```
**Example: Full run from previous activity**
```json
{
"prompt": "Find product specifications",
"source_activity_id": "abc123-previous-activity-id"
}
```
**Large jobs:** send multipart/form-data with a small `request_json` and
one `items_file` JSON upload. `items_file` must be a JSON array, or an
object with an `items` array. Do not send `items_storage_url`; it is internal.
All items are stored in the activity, so full runs can be triggered later using `source_activity_id`.
# Get Enrichment Job
Source: https://docs.rastro.ai/api-reference/enrich/get-enrichment-job
/api-reference/openapi.json get /public/enrich/{job_id}
Poll enrichment job status and get results.
Returns results **progressively** — partial results are available while the job
is still running, as each batch of items completes processing.
**Flow:**
1. Submit enrichment with `async_mode=true` → get `job_id`
2. Poll this endpoint — results appear incrementally as items are processed
3. When `status` changes to `completed`, all results are ready
**Pagination:**
- `page`: Page number (1-indexed, default: 1)
- `page_size`: Results per page (default: 1000)
- Response includes `page`, `page_size`, `total_pages`, `completed_items`
**Statuses:**
- `running`: Job is still processing (partial results may be available)
- `completed`: Job finished, all results ready
- `failed`: Job failed or was cancelled (partial results may still be available)
**Example:**
```
GET /api/public/enrich/{job_id}?page=1&page_size=50
Response (running, partial results):
{
"job_id": "08aa2b0d-...",
"status": "running",
"results": [
{"after_data": {...}, "before_data": {...}, "sources": {...}}
],
"total_items": 500,
"completed_items": 200,
"successful": 0,
"credits_used": 0,
"page": 1,
"page_size": 50,
"total_pages": 4
}
```
# Get Enrichment Template
Source: https://docs.rastro.ai/api-reference/enrich/get-enrichment-template
/api-reference/openapi.json get /public/enrichment-templates/{template_id}
Get enabled template metadata and hydrated config for the authenticated organization.
The list endpoint stays lightweight; this endpoint is used on explicit
selection so the UI can show the template fields and taxonomy before run
time. Large taxonomies can use taxonomy_format=compact for fast selection.
# Infer Enrichment
Source: https://docs.rastro.ai/api-reference/enrich/infer-enrichment
/api-reference/openapi.json post /public/infer-enrichment
Analyze sample data and infer schema, taxonomy, and web enrichment fields.
Pass sample items and a description, get back:
- **suggested_schema**: JSON Schema ready for catalog creation
- **suggested_taxonomy**: Taxonomy ready for catalog creation
- **web_enrichment_fields**: Fields to use with /public/enrich endpoint
**Example request:**
```json
{
"prompt": "Audio products catalog - headphones and earbuds",
"items": [
{"name": "Sony WH-1000XM5", "sku": "WH1000XM5-B", "price": "$399.99"},
{"name": "Apple AirPods Pro 2", "sku": "AIRPODS-PRO-2", "price": "$249.00"}
]
}
```
# Infer Schema From File
Source: https://docs.rastro.ai/api-reference/enrich/infer-schema-from-file
/api-reference/openapi.json post /public/infer-schema
Analyze an uploaded file and infer enrichment configuration.
Upload a CSV or Excel file, and get back:
- **suggested_schema**: JSON Schema with all fields (existing + suggested enrichments)
- **web_enrichment_fields**: Fields to use with /public/enrich endpoint
- **suggested_taxonomy**: Category hierarchy (if generate_taxonomy=true)
This endpoint helps you configure an enrichment pipeline without manual schema definition.
**Example usage:**
1. Upload your data file with a description
2. Use the returned `web_enrichment_fields` as `output_schema` in `/public/enrich`
3. Use `suggested_taxonomy` with `predict_taxonomy=true` in `/public/enrich`
**Supported file formats:**
- CSV (.csv)
- Excel (.xlsx, .xls)
# Judge Catalog Rows
Source: https://docs.rastro.ai/api-reference/enrich/judge-catalog-rows
/api-reference/openapi.json post /public/judge
Judge catalog rows against schema + quality criteria.
When catalog_id is provided, automatically loads the schema and the catalog's
readiness_config quality_prompt. The optional `prompt` field appends extra instructions.
# List Enrichment Jobs
Source: https://docs.rastro.ai/api-reference/enrich/list-enrichment-jobs
/api-reference/openapi.json get /public/enrich/jobs
List enrichment jobs for your organization.
**Query parameters:**
- `status`: Filter by status (`running`, `completed`, `failed`, `cancelled`)
- `limit`: Max results (1-100, default 20)
- `offset`: Pagination offset (default 0)
**Example:**
```
GET /api/public/enrich/jobs?status=running&limit=10
```
# List Enrichment Templates
Source: https://docs.rastro.ai/api-reference/enrich/list-enrichment-templates
/api-reference/openapi.json get /public/enrichment-templates
List enabled enrichment templates for the authenticated organization.
Returns only small manifests. Full configs are storage-backed and are applied
server-side when `template_id` is passed to POST /public/enrich.
# Execute Workflow
Source: https://docs.rastro.ai/api-reference/flows/execute-workflow
/api-reference/openapi.json post /public/workflows/{workflow_id}/execute
Execute a workflow with input data.
- **workflow_id**: The ID of the workflow to execute
- **input**: List of input data records to process
Returns execution info including workflow_run_id for polling.
# Get Workflow Run Status
Source: https://docs.rastro.ai/api-reference/flows/get-workflow-run-status
/api-reference/openapi.json get /public/workflows/runs/{workflow_run_id}
Get status and results of a workflow run.
Returns status info while running, and includes paginated results when completed.
- **workflow_run_id**: The workflow run ID from the execute endpoint
- **page**: Page number for results (default: 1)
- **page_size**: Number of records per page (default: 50, max: 1000)
# List Workflows
Source: https://docs.rastro.ai/api-reference/flows/list-workflows
/api-reference/openapi.json get /public/workflows
List all workflows accessible to the authenticated organization.
Returns workflows owned by the organization associated with the API key or user token.
- **limit**: Maximum number of workflows to return (1-100, default: 50)
- **offset**: Number of workflows to skip for pagination (default: 0)
# Complete API Reference
Source: https://docs.rastro.ai/api-reference/overview
Every public Rastro endpoint in the current API contract
All endpoints use this base URL:
```text theme={null}
https://catalogapi.rastro.ai/api
```
Authenticate every request with:
```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```
The machine-readable OpenAPI contract is available at [`/api-reference/openapi.json`](/api-reference/openapi.json). Use the endpoint-specific pages below for examples and workflow guidance, and use the OpenAPI file when generating clients or checking exact request and response schemas.
## Endpoint Matrix
### Enrich
| Method | Path | Purpose |
| ------ | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `POST` | `/public/enrich` | Start a product enrichment job from inline JSON, uploaded item file, catalog items, or a previous activity |
| `GET` | `/public/enrich/{job_id}` | Poll an enrichment job and retrieve paginated progressive results |
| `GET` | `/public/enrich/jobs` | List enrichment jobs for the organization |
| `POST` | `/public/enrich/{job_id}/cancel` | Cancel a running enrichment job |
| `POST` | `/public/enrich/{job_id}/to-catalog` | Create a new catalog from an enrichment run |
| `GET` | `/public/enrichment-templates` | List enabled enrichment templates |
| `GET` | `/public/enrichment-templates/{template_id}` | Get an enrichment template, optionally with resolved taxonomy |
| `POST` | `/public/infer-enrichment` | Infer schema, taxonomy, and enrichment fields from sample items |
| `POST` | `/public/infer-schema` | Infer catalog and enrichment configuration from an uploaded file |
| `POST` | `/public/judge` | Score catalog rows against a schema and quality prompt |
### Flows
| Method | Path | Purpose |
| ------ | ------------------------------------------ | ------------------------------------------ |
| `GET` | `/public/workflows` | List runnable workflows |
| `POST` | `/public/workflows/{workflow_id}/execute` | Execute a workflow with input records |
| `GET` | `/public/workflows/runs/{workflow_run_id}` | Poll workflow status and paginated results |
### Catalogs
| Method | Path | Purpose |
| -------- | --------------------------------------------------- | ----------------------------------------------------------- |
| `GET` | `/public/catalogs` | List catalogs |
| `POST` | `/public/catalogs` | Create a catalog |
| `GET` | `/public/catalogs/{catalog_id}` | Get catalog details |
| `PUT` | `/public/catalogs/{catalog_id}` | Update catalog settings |
| `DELETE` | `/public/catalogs/{catalog_id}` | Delete a catalog |
| `GET` | `/public/catalogs/{catalog_id}/items` | List transformed public catalog items |
| `POST` | `/public/catalogs/{catalog_id}/items` | Upsert one item by business identifier |
| `GET` | `/public/catalogs/{catalog_id}/items/{item_id}` | Get one transformed public item by database ID |
| `PUT` | `/public/catalogs/{catalog_id}/items/{item_id}` | Update one item by database ID |
| `DELETE` | `/public/catalogs/{catalog_id}/items/{item_id}` | Delete one item by database ID |
| `POST` | `/public/catalogs/{catalog_id}/items/bulk` | Bulk upsert items |
| `GET` | `/public/catalogs/{catalog_id}/raw-items` | List raw catalog rows without public product transformation |
| `GET` | `/public/catalogs/{catalog_id}/raw-items/{item_id}` | Get one raw catalog row |
| `POST` | `/public/catalogs/{catalog_id}/duplicate` | Duplicate a catalog |
### Product Variants
| Method | Path | Purpose |
| ------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `POST` | `/public/catalogs/{catalog_id}/products/{product_id}/items` | Create a variant under a product. `{product_id}` is the parent product item's database ID |
| `PUT` | `/public/catalogs/{catalog_id}/products/{product_id}` | Update product-level fields on the parent product item. `{product_id}` is the database ID |
| `POST` | `/public/catalogs/{catalog_id}/products/{product_id}/items/bulk` | Bulk upsert variants under one product. `{product_id}` is the parent product item's database ID |
### Schema And Taxonomy
| Method | Path | Purpose |
| -------- | --------------------------------------------------- | ------------------------------------------------------ |
| `GET` | `/public/catalogs/{catalog_id}/schema` | Get the current or requested catalog schema version |
| `POST` | `/public/catalogs/{catalog_id}/schema/fields` | Add one schema field |
| `PUT` | `/public/catalogs/{catalog_id}/schema/fields/batch` | Add, update, and remove schema fields in one operation |
| `GET` | `/public/catalogs/{catalog_id}/taxonomy` | Get taxonomy with computed inheritance |
| `PUT` | `/public/catalogs/{catalog_id}/taxonomy` | Set or replace taxonomy |
| `DELETE` | `/public/catalogs/{catalog_id}/taxonomy` | Remove taxonomy |
| `GET` | `/public/catalogs/{catalog_id}/catalog-md` | Get catalog markdown context |
| `PUT` | `/public/catalogs/{catalog_id}/catalog-md` | Update catalog markdown context |
| `GET` | `/public/catalogs/{catalog_id}/quality-prompt` | Get the catalog quality prompt |
| `PUT` | `/public/catalogs/{catalog_id}/quality-prompt` | Update the catalog quality prompt |
### Activities And Snapshots
| Method | Path | Purpose |
| ------ | ---------------------------------------------------------------------- | -------------------------------------- |
| `GET` | `/public/catalogs/{catalog_id}/activities` | List catalog activities |
| `POST` | `/public/catalogs/{catalog_id}/activities` | Create an activity with staged changes |
| `POST` | `/public/catalogs/{catalog_id}/activities/custom-transform` | Create a custom transform activity |
| `POST` | `/public/catalogs/{catalog_id}/activities/{activity_id}/save-workflow` | Save an activity as a workflow |
| `POST` | `/public/activities/{activity_id}/staged-changes/append` | Append staged changes to an activity |
| `POST` | `/public/activities/{activity_id}/pending-review` | Mark an activity ready for review |
| `GET` | `/public/catalogs/{catalog_id}/snapshots` | List catalog snapshots |
| `POST` | `/public/catalogs/{catalog_id}/snapshots` | Create a catalog snapshot |
| `POST` | `/public/catalogs/{catalog_id}/snapshots/{snapshot_id}/restore` | Restore a catalog snapshot |
# Authentication
Source: https://docs.rastro.ai/authentication
Get your API key from [dashboard.rastro.ai/settings/api-keys](https://dashboard.rastro.ai/settings/api-keys).
Include it in every request:
```bash theme={null}
Authorization: Bearer rastro_pk_...
```
Include the organization header when you know the organization ID:
```bash theme={null}
X-Organization-Id: org_...
```
If you omit `X-Organization-Id`, Rastro falls back to the API key or user's primary organization when it can resolve one.
MCP integrations for Codex/Claude use the same API key and only call API endpoints. No direct database credentials are required.
# Catalogs Examples
Source: https://docs.rastro.ai/catalogs/examples
Practical examples for catalog management and enrichment
## Full CRUD Example
```python theme={null}
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://catalogapi.rastro.ai/api"
headers = {"Authorization": f"Bearer {API_KEY}"}
# 1. Get your catalog ID from the dashboard or list catalogs
catalogs = requests.get(f"{BASE_URL}/public/catalogs", headers=headers).json()
catalog_id = catalogs["catalogs"][0]["id"]
# 2. Add items
items = [
{"sku": "BEARING-001", "name": "6205-2RS Ball Bearing", "price": 12.99},
{"sku": "BEARING-002", "name": "6206-2RS Ball Bearing", "price": 15.99},
{"sku": "BEARING-003", "name": "6207-2RS Ball Bearing", "price": 18.99}
]
result = requests.post(
f"{BASE_URL}/public/catalogs/{catalog_id}/items/bulk",
headers=headers,
json={"items": items}
).json()
print(f"Added {result['items_created']} items")
# 3. List items
items = requests.get(
f"{BASE_URL}/public/catalogs/{catalog_id}/items",
headers=headers
).json()
for item in items["items"]:
data = item["data"]
print(f" {data['sku']}: {data['name']} - ${data['price']}")
# 4. Update an item
item_id = items["items"][0]["id"]
updated = requests.put(
f"{BASE_URL}/public/catalogs/{catalog_id}/items/{item_id}",
headers=headers,
json={"price": 10.99}
).json()
updated_data = updated["data"]
print(f"Updated {updated_data['sku']} price to ${updated_data['price']}")
# 5. Get single item
item = requests.get(
f"{BASE_URL}/public/catalogs/{catalog_id}/items/{item_id}",
headers=headers
).json()
print(f"Item: {item}")
```
***
## Catalog Enrichment with Polling
Use `POST /public/enrich` with `catalog_id` to enrich catalog items. The catalog's schema is applied automatically.
```python theme={null}
import requests
import time
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://catalogapi.rastro.ai/api"
headers = {"Authorization": f"Bearer {API_KEY}"}
def enrich_catalog(catalog_id, items, speed="deep"):
# Start enrichment using the catalog's schema
response = requests.post(
f"{BASE_URL}/public/enrich",
headers=headers,
json={
"catalog_id": catalog_id,
"items": items,
"async_mode": True,
"speed": speed
}
)
job = response.json()
job_id = job["job_id"]
print(f"Started enrichment job: {job_id}")
print(f"Total items: {job['total_items']}")
# Poll for completion
while True:
status = requests.get(
f"{BASE_URL}/public/enrich/{job_id}",
headers=headers
).json()
completed = status.get("completed_items", 0)
total = status.get("total_items", 0)
print(f"Progress: {completed}/{total}")
if status["status"] != "running":
return status
time.sleep(10)
# Example usage
catalog_id = "cat_abc123"
items = [
{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
{"part_number": "6206-2RS", "name": "Deep Groove Ball Bearing"},
{"part_number": "6207-2RS", "name": "Deep Groove Ball Bearing"}
]
result = enrich_catalog(catalog_id, items)
print(f"\nEnrichment {result['status']}")
print(f"Completed: {result['completed_items']}")
```
***
## Full Python Client
```python theme={null}
import requests
import time
class CatalogClient:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://catalogapi.rastro.ai/api"
def _headers(self):
return {"Authorization": f"Bearer {self.api_key}"}
# Catalog operations
def list_catalogs(self):
response = requests.get(
f"{self.base_url}/public/catalogs",
headers=self._headers()
)
return response.json()["catalogs"]
# Item operations
def list_items(self, catalog_id, offset=0, limit=100):
response = requests.get(
f"{self.base_url}/public/catalogs/{catalog_id}/items",
headers=self._headers(),
params={"offset": offset, "limit": limit}
)
return response.json()
def get_item(self, catalog_id, item_id):
response = requests.get(
f"{self.base_url}/public/catalogs/{catalog_id}/items/{item_id}",
headers=self._headers()
)
return response.json()
def update_item(self, catalog_id, item_id, data):
response = requests.put(
f"{self.base_url}/public/catalogs/{catalog_id}/items/{item_id}",
headers=self._headers(),
json=data
)
return response.json()
def bulk_upsert(self, catalog_id, items):
response = requests.post(
f"{self.base_url}/public/catalogs/{catalog_id}/items/bulk",
headers=self._headers(),
json={"items": items}
)
return response.json()
# Enrichment (uses the Enrich API with catalog_id)
def start_enrichment(self, catalog_id, items, speed="deep", **kwargs):
response = requests.post(
f"{self.base_url}/public/enrich",
headers=self._headers(),
json={"catalog_id": catalog_id, "items": items, "async_mode": True, "speed": speed, **kwargs}
)
return response.json()
def get_enrichment_status(self, job_id):
response = requests.get(
f"{self.base_url}/public/enrich/{job_id}",
headers=self._headers()
)
return response.json()
def enrich_and_wait(self, catalog_id, items, poll_interval=10, **kwargs):
job = self.start_enrichment(catalog_id, items, **kwargs)
job_id = job["job_id"]
print(f"Started job: {job_id}")
while True:
status = self.get_enrichment_status(job_id)
print(f"Progress: {status.get('completed_items', 0)}/{status.get('total_items', '?')}")
if status["status"] != "running":
return status
time.sleep(poll_interval)
# Example usage
client = CatalogClient("YOUR_API_KEY")
# Get catalog ID from dashboard or list
catalogs = client.list_catalogs()
catalog_id = catalogs[0]["id"]
# Add items
items = [
{"sku": "6205-2RS", "name": "Deep Groove Ball Bearing"},
{"sku": "6206-2RS", "name": "Deep Groove Ball Bearing"},
{"sku": "6207-2RS", "name": "Deep Groove Ball Bearing"}
]
client.bulk_upsert(catalog_id, items)
# Enrich and wait
result = client.enrich_and_wait(
catalog_id,
items=items,
speed="deep"
)
# List enriched items
catalog_items = client.list_items(catalog_id)
for item in catalog_items["items"]:
data = item["data"]
print(f"{data['sku']}: bore={data.get('bore_diameter', 'N/A')}")
```
***
## Full TypeScript Client
```typescript theme={null}
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://catalogapi.rastro.ai/api";
class CatalogClient {
private apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
private headers() {
return {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json"
};
}
// Catalog operations
async listCatalogs() {
const response = await fetch(`${BASE_URL}/public/catalogs`, {
headers: this.headers()
});
const data = await response.json();
return data.catalogs;
}
// Item operations
async listItems(catalogId: string, offset = 0, limit = 100) {
const response = await fetch(
`${BASE_URL}/public/catalogs/${catalogId}/items?offset=${offset}&limit=${limit}`,
{ headers: this.headers() }
);
return response.json();
}
async getItem(catalogId: string, itemId: string) {
const response = await fetch(
`${BASE_URL}/public/catalogs/${catalogId}/items/${itemId}`,
{ headers: this.headers() }
);
return response.json();
}
async updateItem(catalogId: string, itemId: string, data: object) {
const response = await fetch(
`${BASE_URL}/public/catalogs/${catalogId}/items/${itemId}`,
{
method: "PUT",
headers: this.headers(),
body: JSON.stringify(data)
}
);
return response.json();
}
async bulkUpsert(catalogId: string, items: object[]) {
const response = await fetch(
`${BASE_URL}/public/catalogs/${catalogId}/items/bulk`,
{
method: "POST",
headers: this.headers(),
body: JSON.stringify({ items, source_info: { source: "api" } })
}
);
return response.json();
}
// Enrichment (uses the Enrich API with catalog_id)
async startEnrichment(catalogId: string, items: object[], options: object = {}) {
const response = await fetch(
`${BASE_URL}/public/enrich`,
{
method: "POST",
headers: this.headers(),
body: JSON.stringify({ catalog_id: catalogId, items, async_mode: true, ...options })
}
);
return response.json();
}
async getEnrichmentStatus(jobId: string) {
const response = await fetch(
`${BASE_URL}/public/enrich/${jobId}`,
{ headers: this.headers() }
);
return response.json();
}
async enrichAndWait(
catalogId: string,
items: object[],
options: object = {},
pollInterval = 10000
) {
const job = await this.startEnrichment(catalogId, items, options);
const jobId = job.job_id;
console.log(`Started job: ${jobId}`);
while (true) {
const status = await this.getEnrichmentStatus(jobId);
console.log(`Progress: ${status.completed_items || 0}/${status.total_items || "?"}`);
if (status.status !== "running") {
return status;
}
await new Promise(r => setTimeout(r, pollInterval));
}
}
}
// Example usage
const client = new CatalogClient("YOUR_API_KEY");
// Get catalog ID from dashboard or list
const catalogs = await client.listCatalogs();
const catalogId = catalogs[0].id;
// Add items
const items = [
{ sku: "6205-2RS", name: "Deep Groove Ball Bearing" },
{ sku: "6206-2RS", name: "Deep Groove Ball Bearing" }
];
await client.bulkUpsert(catalogId, items);
// Enrich and wait
const result = await client.enrichAndWait(
catalogId,
items,
{ speed: "deep" }
);
// List enriched items
const enrichedItems = await client.listItems(catalogId);
for (const item of enrichedItems.items) {
console.log(`${item.data.sku}: bore=${item.data.bore_diameter || "N/A"}`);
}
```
# Catalogs Quickstart
Source: https://docs.rastro.ai/catalogs/quickstart
Store and manage your product data
Catalogs store your product data in Rastro. Create a catalog, add items, then enrich them with Flows or the Enrich API.
## 1. Create a catalog
Create a catalog in the [Rastro dashboard](https://dashboard.rastro.ai), or via the API:
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/catalogs",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"name": "My Products",
"description": "Product catalog",
"unique_id_field": "sku",
"auto_evolve_schema": True,
"schema_definition": {
"properties": {
"sku": {"type": "string", "description": "Stock keeping unit"},
"title": {"type": "string", "description": "Product name"},
"price": {"type": "number", "description": "Price"}
},
"required": ["sku"]
}
}
)
catalog_id = response.json()["id"]
```
Once created, copy your `catalog_id` to use in API calls below.
`unique_id_field` is a required legacy compatibility field on public catalog creation. Keep the actual business key, such as `sku`, in your schema and item data.
## 2. Add items (bulk)
```python Python theme={null}
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items/bulk",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [
{"sku": "A1", "title": "Product A", "price": 29.99},
{"sku": "A2", "title": "Product B", "price": 39.99},
{"sku": "A3", "title": "Product C", "price": 49.99}
]
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs/cat_abc123/items/bulk" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"sku": "A1", "title": "Product A", "price": 29.99},
{"sku": "A2", "title": "Product B", "price": 39.99},
{"sku": "A3", "title": "Product C", "price": 49.99}
]
}'
```
```typescript TypeScript theme={null}
const response = await fetch(`https://catalogapi.rastro.ai/api/public/catalogs/${catalogId}/items/bulk`, {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
items: [
{ sku: "A1", title: "Product A", price: 29.99 },
{ sku: "A2", title: "Product B", price: 39.99 },
{ sku: "A3", title: "Product C", price: 49.99 }
]
})
});
console.log(await response.json());
```
**Response:**
```json theme={null}
{
"success": true,
"items_processed": 3,
"items_created": 3,
"items_failed": 0,
"schema_evolved": false
}
```
There is no `unique_field` query parameter. Put your business key fields, such as `sku`, directly in each item object.
## 3. List your items
```python Python theme={null}
response = requests.get(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
print(response.json())
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs/cat_abc123/items" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
const response = await fetch(`https://catalogapi.rastro.ai/api/public/catalogs/${catalogId}/items`, {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
console.log(await response.json());
```
**Response:**
```json theme={null}
{
"items": [
{"id": "item_1", "data": {"title": "Product A", "price": 29.99, ...}, "metadata": {...}},
{"id": "item_2", "data": {"title": "Product B", "price": 39.99, ...}, "metadata": {...}}
],
"total": 3,
"page": 0,
"limit": 50
}
```
## 4. Update an item
```python Python theme={null}
response = requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items/{item_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"price": 24.99}
)
print(response.json())
```
```bash cURL theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/cat_abc123/items/item_1" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"price": 24.99}'
```
```typescript TypeScript theme={null}
const response = await fetch(`https://catalogapi.rastro.ai/api/public/catalogs/${catalogId}/items/${itemId}`, {
method: "PUT",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ price: 24.99 })
});
console.log(await response.json());
```
## 5. Configure your catalog
Update catalog settings, schema, taxonomy, and context:
```python theme={null}
# Update catalog settings and context
requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"catalog_md": "# Catalog Context\nThis catalog contains industrial bearings.",
"auto_evolve_schema": True
}
)
# Add a field to the schema
requests.post(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema/fields",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"field_name": "weight_kg",
"field_type": "number",
"description": "Product weight in kilograms",
"unit": "kg"
}
)
# Set a taxonomy for categorization
requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/taxonomy",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"name": "Product Categories",
"hierarchy_levels": ["Category", "Subcategory"],
"nodes": {
"bearings": {"name": "Bearings", "parent": None, "attributes": []},
"ball_bearings": {"name": "Ball Bearings", "parent": "bearings", "attributes": [
{"name": "bore_diameter", "type": "number", "unit": "mm"}
]}
}
}
)
```
***
## Key Concepts
* **Schema** — Defines the structure of your catalog items. Fields can be added/updated/removed via the API.
* **Taxonomy** — Optional hierarchical category tree with per-category attributes. Used for enrichment and classification.
* **Catalog MD** — Markdown context injected into enrichment and mapping prompts to guide AI behavior.
* **Bulk upsert** — Add up to 1000 items at once. Items are matched by the catalog's unique ID field.
* **Catalogs + Flows** — Connect a catalog to a Flow to automatically enrich items when they're added.
***
## What's Next
All endpoints and parameters
Full CRUD and enrichment examples
Agent-driven bulk edits with staged review
# Catalogs API Reference
Source: https://docs.rastro.ai/catalogs/reference
Complete API documentation for Catalog endpoints
## Catalog Management
### POST /public/catalogs
Create a catalog with an initial JSON Schema.
```python Python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/catalogs",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"name": "Products",
"description": "Main product catalog",
"unique_id_field": "sku",
"product_id_field": "product_id",
"auto_evolve_schema": True,
"strict_mode": False,
"schema_definition": {
"properties": {
"sku": {"type": "string", "description": "Stock keeping unit"},
"title": {"type": "string", "description": "Product title"},
"price": {"type": "number", "description": "Product price"}
},
"required": ["sku"]
}
}
)
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Products",
"unique_id_field": "sku",
"schema_definition": {
"properties": {
"sku": {"type": "string"},
"title": {"type": "string"}
},
"required": ["sku"]
}
}'
```
**Request Parameters:**
| Parameter | Type | Required | Description |
| ----------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Yes | Catalog name (1-100 chars) |
| `description` | string | No | Optional description |
| `unique_id_field` | string | Yes | Legacy compatibility field required by the public create request. Include the intended business key, such as `sku`, in your schema and item data; this request field is not a separate runtime selector |
| `product_id_field` | string | No | Field used to group variants under products |
| `auto_evolve_schema` | boolean | No | Allow automatic schema evolution |
| `strict_mode` | boolean | No | Reject unknown fields |
| `schema_definition` | object | Yes | Initial JSON Schema with `properties` and optional `required` |
| `validation_rules` | object | No | Catalog-level validation rules |
| `catalog_md` | string | No | Markdown context for enrichment/mapping prompts |
| `master_catalog_id` | string | No | Master catalog to inherit defaults from |
| `use_master_catalog_defaults` | boolean | No | Whether to inherit defaults from the org default master catalog |
**Response:** The created catalog object.
***
### GET /public/catalogs
List all catalogs in your account.
```python Python theme={null}
response = requests.get(
"https://catalogapi.rastro.ai/api/public/catalogs",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"limit": 50, "offset": 0}
)
print(response.json())
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs?limit=50&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Query Parameters:**
| Parameter | Type | Default | Description |
| --------- | ------- | ------- | ----------------------- |
| `limit` | integer | 10 | Items per page (1–100) |
| `offset` | integer | 0 | Number of items to skip |
**Response:**
```json theme={null}
{
"catalogs": [
{
"id": "cat_123",
"name": "Products",
"description": "Main product catalog",
"auto_evolve_schema": true,
"strict_mode": false,
"item_count": 150,
"current_schema_version": "1.0",
"created_at": "2026-01-15T10:30:00Z"
}
],
"total_count": 1,
"page": 1,
"page_size": 50
}
```
***
### GET /public/catalogs/
Get details of a specific catalog.
**Response:** Same shape as a single catalog object from the list endpoint.
***
### PUT /public/catalogs/
Update catalog settings. Only provided fields are changed; omitted fields remain unchanged.
```python Python theme={null}
response = requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"name": "Renamed Catalog",
"description": "Updated description",
"auto_evolve_schema": False,
"catalog_md": "# Catalog Context\nThis catalog contains industrial bearings."
}
)
```
```bash cURL theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Renamed Catalog", "description": "Updated description"}'
```
**Request Parameters:**
| Parameter | Type | Required | Description |
| -------------------- | ------- | -------- | --------------------------------------------------------- |
| `name` | string | No | New catalog name (1–100 chars) |
| `description` | string | No | Updated description |
| `auto_evolve_schema` | boolean | No | Allow automatic schema evolution |
| `strict_mode` | boolean | No | Reject unknown fields |
| `catalog_md` | string | No | Markdown context injected into enrichment/mapping prompts |
**Response:** The updated catalog object.
***
### DELETE /public/catalogs/
Delete a catalog and all its data (items, schema, snapshots).
```bash theme={null}
curl -X DELETE "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Response:**
```json theme={null}
{"message": "Catalog cat_123 deleted successfully"}
```
***
## Schema Management
### GET /public/catalogs//schema
Get the current schema definition, including field metadata and workflow tracking.
```bash theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Response:**
```json theme={null}
{
"version": "1.2",
"schema_definition": {
"properties": {
"sku": {"type": "string", "description": "Stock keeping unit"},
"price": {"type": "number", "description": "Product price"}
},
"required": ["sku"]
},
"total_fields": 2,
"input_fields_count": 1,
"generated_fields_count": 1,
"fields_workflow_info": [...]
}
```
***
### POST /public/catalogs//schema/fields
Add a new field to the catalog schema. Creates a new schema version.
```python Python theme={null}
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema/fields",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"field_name": "weight_kg",
"field_type": "number",
"description": "Product weight in kilograms",
"required": False,
"field_category": "input",
"unit": "kg"
}
)
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema/fields" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"field_name": "weight_kg", "field_type": "number", "description": "Product weight"}'
```
**Request Parameters:**
| Parameter | Type | Required | Description |
| ------------------ | ------- | -------- | ------------------------------------------------------------------------------------------- |
| `field_name` | string | Yes | Name of the field |
| `field_type` | string | Yes | Type such as `string`, `number`, `integer`, `boolean`, `array`, or `object` |
| `description` | string | No | Field description |
| `required` | boolean | No | Whether the field is required (default: false) |
| `field_category` | string | No | `input` or `generated`. Stored as workflow-tracking metadata when `workflow_id` is provided |
| `position` | string | No | Where to add the field: `top` or `bottom` |
| `workflow_id` | string | No | Workflow ID associated with the schema change |
| `unit` | string | No | Unit of measurement (e.g., `kg`, `V`, `W`) |
| `sample_values` | array | No | Example values for the field |
| `validation_rules` | object | No | JSON Schema validation (pattern, enum, etc.) |
**Response:**
```json theme={null}
{
"success": true,
"catalog_id": "cat_123",
"new_schema_version": "1.3",
"previous_schema_version": "1.2",
"fields_added": ["weight_kg"],
"message": "Field 'weight_kg' added successfully. New schema version: 1.3"
}
```
***
### PUT /public/catalogs//schema/fields/batch
Add, update, and/or remove multiple fields in one atomic operation.
```python Python theme={null}
response = requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema/fields/batch",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"fields_to_add": [
{"field_name": "color", "field_type": "string", "description": "Product color"},
{"field_name": "weight", "field_type": "number", "unit": "kg"}
],
"fields_to_update": [
{"field_name": "price", "description": "Updated price description"}
],
"fields_to_remove": ["old_field"],
"reason": "Schema cleanup"
}
)
```
```bash cURL theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/schema/fields/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fields_to_add": [{"field_name": "color", "field_type": "string"}],
"fields_to_remove": ["old_field"],
"reason": "Schema cleanup"
}'
```
**Request Parameters:**
| Parameter | Type | Required | Description |
| ------------------ | ------ | -------- | ------------------------------------------------------------ |
| `fields_to_add` | array | No | Fields to add (same shape as POST schema/fields) |
| `fields_to_update` | array | No | Fields to update (include `field_name` + changed attributes) |
| `fields_to_remove` | array | No | Field names to remove |
| `reason` | string | No | Reason for the changes |
**Response:** Same shape as POST schema/fields response.
***
## Taxonomy Management
### GET /public/catalogs//taxonomy
Get the catalog's taxonomy with computed inheritance (levels, paths, inherited attributes).
Returns `null` if no taxonomy is configured.
```bash theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/taxonomy" \
-H "Authorization: Bearer YOUR_API_KEY"
```
Supports ETag caching via `If-None-Match` header — returns `304 Not Modified` when unchanged.
***
### PUT /public/catalogs//taxonomy
Set or replace the catalog taxonomy. Creates a new schema version.
```python Python theme={null}
response = requests.put(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/taxonomy",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"name": "Product Categories",
"hierarchy_levels": ["Category", "Subcategory"],
"nodes": {
"electronics": {
"name": "Electronics",
"parent": None,
"attributes": []
},
"phones": {
"name": "Phones",
"parent": "electronics",
"attributes": [
{"name": "brand", "type": "enum", "values": ["Apple", "Samsung"]}
]
}
}
}
)
```
```bash cURL theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/taxonomy" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Categories",
"hierarchy_levels": ["Category"],
"nodes": {
"electronics": {"name": "Electronics", "parent": null, "attributes": []}
}
}'
```
**Response:** The enriched taxonomy with computed fields (level, path, children, inherited\_attributes).
***
### DELETE /public/catalogs//taxonomy
Remove taxonomy from the catalog. Creates a new schema version without taxonomy.
```bash theme={null}
curl -X DELETE "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/taxonomy" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Response:** `204 No Content`
***
## Catalog Context
### GET /public/catalogs//catalog-md
Get the catalog's markdown context (injected into enrichment and mapping prompts).
```bash theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/catalog-md" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Response:**
```json theme={null}
{"catalog_id": "cat_123", "catalog_md": "# Product Context\nThis catalog contains..."}
```
***
### PUT /public/catalogs//catalog-md
Update the catalog's markdown context directly (versioned).
```bash theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/catalog-md" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"catalog_md": "# Updated Context\nNew instructions for enrichment."}'
```
You can also update `catalog_md` via `PUT /public/catalogs/{catalog_id}` by including the `catalog_md` field in the request body.
***
### GET /public/catalogs//quality-prompt
Get the catalog's quality prompt (used by the judge and readiness checks).
### PUT /public/catalogs//quality-prompt
Set the quality prompt.
```json theme={null}
{"prompt": "Rate completeness of product specs on a 1-5 scale..."}
```
***
## Item Management
### GET /public/catalogs//items
List items in a catalog with pagination, search, and sorting.
```python Python theme={null}
response = requests.get(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"limit": 50, "offset": 0, "sort": "created_at:desc"}
)
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items?limit=50&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
```
**Query Parameters:**
| Parameter | Type | Default | Description |
| -------------- | ------- | ------- | --------------------------------------------------- |
| `limit` | integer | 50 | Items per page (1–1000) |
| `offset` | integer | 0 | Number of items to skip |
| `search` | string | — | Text search across all fields |
| `search_query` | string | — | Alias for `search` |
| `sort` | string | — | Sort by `field:direction` (e.g., `created_at:desc`) |
**Response:**
```json theme={null}
{
"items": [
{
"id": "item_123",
"data": {"title": "Product A", "price": 29.99, "variants": [...]},
"metadata": {"created_at": "2026-01-15T10:30:00Z", "updated_at": null, "version": null}
}
],
"total": 150,
"page": 0,
"limit": 50
}
```
***
### GET /public/catalogs//items/
Get a single item by database ID.
**Response:** Single item in the same shape as list items.
***
### POST /public/catalogs//items
Upsert a single item. Put the business identifier inside `data` (for example, `{"sku": "A1"}`); the legacy `unique_identifier` field is still required by the request shape but is not used as a separate selector.
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"unique_identifier": "A1",
"data": {"sku": "A1", "title": "Product A", "price": 29.99},
"source_info": {"source": "public_api"}
}'
```
| Parameter | Type | Required | Description |
| ------------------- | ------ | -------- | ------------------------------------------------------------------------------- |
| `unique_identifier` | string | Yes | Legacy compatibility field. Send the same value as the identifier inside `data` |
| `data` | object | Yes | Item data to write |
| `source_info` | object | No | Metadata recorded with the write |
**Response:** The created or updated item.
***
### PUT /public/catalogs//items/
Update a single item. Only the fields you provide will be updated.
**Request Body:** Any fields to update (dynamic schema).
**Response:** The updated item.
***
### DELETE /public/catalogs//items/
Delete a single item.
**Response:**
```json theme={null}
{"message": "Item deleted successfully"}
```
***
### POST /public/catalogs//items/bulk
Create or update up to 1000 items at once. Include your business key fields, such as `sku`, in each item object.
```python Python theme={null}
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items/bulk",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [
{"sku": "A1", "title": "Product A", "price": 29.99},
{"sku": "A2", "title": "Product B", "price": 39.99}
]
}
)
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/items/bulk" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"items": [{"sku": "A1", "title": "Product A", "price": 29.99}]}'
```
**Request Parameters:**
| Parameter | Type | Required | Description |
| -------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `items` | array | Yes | Array of item objects (max 1000) |
| `source_info` | object | No | Metadata about the data source |
| `auto_evolve_schema` | boolean | No | Accepted for compatibility. Public bulk writes currently allow schema evolution from the submitted item data |
There is no `unique_field` query parameter. Put the business key fields directly in each item object.
**Response:**
```json theme={null}
{
"success": true,
"items_processed": 15,
"items_created": 10,
"items_failed": 0,
"schema_evolved": false,
"errors": []
}
```
***
## Product Variant Management
Use these endpoints when a catalog groups variants under a product item. The `{product_id}` path parameter is the parent product item's database ID, not the business `product_id` field value stored in row data.
### POST /public/catalogs//products//items
Create one variant under a product.
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/products/{product_item_id}/items" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sku":"SHIRT-001-RED-M","color":"Red","size":"M","price":29.99}'
```
### PUT /public/catalogs//products/
Update product-level fields shared by variants.
```bash theme={null}
curl -X PUT "https://catalogapi.rastro.ai/api/public/catalogs/{catalog_id}/products/{product_item_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"product_title":"Classic T-Shirt","product_brand":"MyBrand"}'
```
### POST /public/catalogs//products//items/bulk
Bulk upsert variants for one product.
```json theme={null}
{
"product": {
"product_title": "Classic T-Shirt",
"product_brand": "MyBrand"
},
"variants": [
{"sku": "SHIRT-001-RED-M", "color": "Red", "size": "M"},
{"sku": "SHIRT-001-BLUE-M", "color": "Blue", "size": "M"}
],
"source_info": {"source": "public_api"},
"auto_evolve_schema": true
}
```
**Response:**
```json theme={null}
{
"product_id": "SHIRT-001",
"items_processed": 2,
"items_created": 2,
"items_updated": 0,
"items_failed": 0,
"schema_evolved": false,
"errors": [],
"warnings": []
}
```
***
## Enriching Catalog Items
To enrich items in a catalog, use the [Enrich API](/enrich/reference) with the `catalog_id` parameter. The catalog's schema and taxonomy are automatically applied.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"catalog_id": "cat_abc123",
"items": [
{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}
],
"speed": "deep"
}
)
```
See [Enrich Examples](/enrich/examples#reuse-configuration-with-catalog-id) for more details.
***
## Snapshots
### GET /public/catalogs//snapshots
List catalog snapshots.
### POST /public/catalogs//snapshots
Create a snapshot. Request body: `{"reason": "Before migration"}`.
### POST /public/catalogs//snapshots//restore
Restore from a snapshot. Automatically creates a safety snapshot first.
***
## MCP-Oriented Endpoints
These endpoints are commonly used by Rastro MCP servers for large catalog workflows:
* `GET /public/catalogs/{catalog_id}/raw-items` for full-fidelity catalog row pulls. Supports `limit` (1-5000, default 1000), `offset`, `entity_type=product|variant`, `search`, `sort_field`, and `sort_order=asc|desc`
* `GET /public/catalogs/{catalog_id}/raw-items/{item_id}` for a single raw catalog row
* `GET /public/catalogs/{catalog_id}/activities` to audit pending/completed activities
* `POST /public/catalogs/{catalog_id}/activities` to create activity shells
* `POST /public/catalogs/{catalog_id}/activities/custom-transform` to create a custom transform activity
* `POST /public/catalogs/{catalog_id}/activities/{activity_id}/save-workflow` to save an activity as a workflow
* `POST /public/activities/{activity_id}/staged-changes/append` to append staged changes in chunks
* `POST /public/activities/{activity_id}/pending-review` to finalize one review activity
Activity listing supports `status`, `type`, `limit`, and `offset` query parameters. Snapshots support `snapshot_type`, `limit`, and `offset`.
See [MCP Reference](/mcp/reference) for full workflow guidance.
# Credits
Source: https://docs.rastro.ai/credits
How credits work — monthly allowance, credit packs, costs, and usage monitoring
Rastro meters enrichment with **credits**. You only pay for successful results: credits are charged per **successfully enriched SKU**, and cached or reused results are never charged again.
## What enrichment costs
| Operation | Credits per successful SKU |
| ------------------------------------ | -------------------------- |
| Standard enrichment (taxonomy + web) | 7 |
| Web enrichment only | 5 |
| Taxonomy enrichment only | 3 |
| Non-web enrichment | 1 |
| Monitoring extraction | 1 |
| Monitoring check | Free |
Failed SKUs are not charged. Re-running an enrichment that hits cached results is not charged again.
## Where credits come from
Your organization draws from two pools, in order:
1. **Monthly allowance** — credits included with your plan. The allowance resets at the start of each calendar month and unused credits **do not roll over**. The size of the allowance depends on your plan.
2. **Credit packs** — credits you purchase on top of your allowance. Packs **roll over** until they are spent, and are only drawn down once your monthly allowance for the current month is used up.
## Monitoring usage
Go to [**Settings → Credits**](https://dashboard.rastro.ai/settings/credits) in your dashboard to see your organization's:
* **Total available credits**, split by monthly allowance and credit packs
* **Usage over time** — hourly, daily, weekly, or monthly, for any date range
* **Breakdown by purpose** — which features consumed credits
* **Breakdown by user** — which team members drove usage
All figures are scoped to the organization you currently have selected.
## Photo Studio credits
Image and video generation in [Photo Studio](/photo-studio/quickstart) draws from the **same shared credit balance**, with tool-specific costs (see the [cost table](/photo-studio/reference#credits-and-costs)). Photo Studio shows the estimated cost of each operation next to your remaining balance before you apply it, and only successful generations are charged.
## Need more credits?
Contact [support@rastro.ai](mailto:support@rastro.ai?subject=Add%20Credits) to add a credit pack or adjust your monthly allowance.
# Enrich Examples
Source: https://docs.rastro.ai/enrich/examples
Practical examples for common enrichment use cases
## Basic Enrichment
```python Python theme={null}
import requests
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"part_number": "91251A545", "name": "Hex Head Cap Screw"}],
"output_schema": [
{"name": "material", "type": "string", "description": "Material composition"},
{"name": "thread_size", "type": "string", "description": "Thread size specification"},
{"name": "length", "type": "string", "description": "Screw length"}
],
"speed": "deep"
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [{"part_number": "91251A545", "name": "Hex Head Cap Screw"}],
"output_schema": [
{"name": "material", "type": "string", "description": "Material composition"},
{"name": "thread_size", "type": "string", "description": "Thread size specification"},
{"name": "length", "type": "string", "description": "Screw length"}
],
"speed": "deep"
}'
```
***
## Reuse Configuration with Catalog ID
Use `catalog_id` to automatically apply a catalog's schema, taxonomy, and settings without repeating them in every request.
```python theme={null}
# Create a catalog with schema and settings via the Rastro dashboard,
# then use its catalog_id to enrich without repeating output_schema.
catalog_id = "cat_abc123" # From dashboard
# Enrich using just the catalog_id - no need to repeat output_schema
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"catalog_id": catalog_id,
"items": [
{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
{"part_number": "6206-2RS", "name": "Deep Groove Ball Bearing"}
],
"speed": "deep"
}
)
print(response.json())
```
The catalog's schema fields are automatically used as the `output_schema`. Any taxonomy or quality settings configured on the catalog are also applied. Catalogs are created through the [Rastro dashboard](https://dashboard.rastro.ai).
***
## Multiple Items
Items are processed concurrently for speed.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [
{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
{"part_number": "6206-2RS", "name": "Deep Groove Ball Bearing"},
{"part_number": "6207-2RS", "name": "Deep Groove Ball Bearing"}
],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"},
{"name": "width", "type": "string", "description": "Bearing width in mm"}
],
"speed": "deep"
}
)
```
***
## Domain Restrictions
Only pull data from specific manufacturer or distributor sites.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"part_number": "6ES7214-1AG40-0XB0", "name": "SIMATIC S7-1200 CPU"}],
"output_schema": [
{"name": "input_voltage", "type": "string", "description": "Operating voltage range"},
{"name": "digital_inputs", "type": "integer", "description": "Number of digital inputs"},
{"name": "memory", "type": "string", "description": "Work memory size"}
],
"allowed_domains": ["siemens.com", "automation.siemens.com"],
"speed": "deep"
}
)
```
***
## Data Normalization (No Web Search)
Normalize and structure data from your existing sources without web lookups. Sources trace back to your input fields.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{
"raw_title": "SKF 6205-2RS Deep Groove Ball Bearing 25x52x15mm",
"description": "Sealed bearing for high-speed applications"
}],
"output_schema": [
{"name": "manufacturer", "type": "string", "description": "Brand name"},
{"name": "bore_diameter", "type": "string", "description": "Inner diameter"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter"},
{"name": "width", "type": "string", "description": "Width"}
],
"web_search": False # Only use input data
}
)
# Response sources will reference INPUT_DATA
```
**Response:**
```json theme={null}
{
"after_data": {
"manufacturer": "SKF",
"bore_diameter": "25mm",
"outer_diameter": "52mm",
"width": "15mm",
"sources": {
"manufacturer": ["INPUT_DATA"],
"bore_diameter": ["INPUT_DATA"],
"outer_diameter": ["INPUT_DATA"],
"width": ["INPUT_DATA"]
}
}
}
```
***
## Async Mode
For large batches, get a job ID immediately and poll for results. Results are available **progressively** — you can access completed items while the job is still running.
```python theme={null}
import requests
import time
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://catalogapi.rastro.ai/api"
# Start async job
response = requests.post(
f"{BASE_URL}/public/enrich",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"items": [
{"part_number": "6205-2RS"},
{"part_number": "6206-2RS"},
{"part_number": "6207-2RS"},
# ... hundreds more items
],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter"}
],
"async_mode": True,
"speed": "deep"
}
)
job_id = response.json()["job_id"]
print(f"Job started: {job_id}")
# Poll for results — partial results are available while running
while True:
status = requests.get(
f"{BASE_URL}/public/enrich/{job_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"page": 1, "page_size": 50}
).json()
print(f"Status: {status['status']} — "
f"{status.get('completed_items', 0)}/{status['total_items']} items ready")
if status["status"] != "running":
break
time.sleep(5)
# Paginate through all results
all_results = []
page = 1
while True:
resp = requests.get(
f"{BASE_URL}/public/enrich/{job_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"page": page, "page_size": 100}
).json()
all_results.extend(resp["results"])
if page >= resp["total_pages"]:
break
page += 1
print(f"Fetched {len(all_results)} results, {resp['credits_used']} credits used")
```
***
## Taxonomy Prediction
Automatically classify items into your category hierarchy.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"name": "SKF 6205-2RS Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter"}
],
"predict_taxonomy": True,
"taxonomy": {
"name": "Industrial Parts",
"hierarchy_levels": ["Category", "Type"],
"nodes": {
"bearings": {"name": "Bearings", "parent": None},
"ball_bearings": {
"name": "Ball Bearings",
"parent": "bearings",
"attributes": [
{"name": "Bore Size", "type": "string"},
{"name": "Seal Type", "type": "string"}
]
}
}
}
}
)
```
**Response includes:**
```json theme={null}
{
"category_id": "ball_bearings",
"category_path": "Ball Bearings",
"taxonomy_attributes": {
"Bore Size": "25 mm",
"Seal Type": "2RS (rubber seal)"
}
}
```
***
## Row Judging
Use `POST /public/judge` to evaluate rows against a catalog schema and quality prompt. Pass `catalog_id` to reuse the catalog's schema and saved quality prompt, or pass an inline `schema`.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/judge",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"catalog_id": "cat_123",
"rows": [
{"part_number": "6205-2RS", "bore_diameter": "25 mm", "load_rating": ""}
],
"prompt": "Evaluate if product has complete specs for procurement",
"max_rows": 50
}
)
```
**Response includes:**
```json theme={null}
{
"judgments": [
{
"row_index": 0,
"decision": "review_required",
"reasons": ["Missing dynamic load rating"],
"field_issues": ["load_rating is blank"]
}
],
"meta": {
"model": "fast",
"rows_judged": 1
}
}
```
***
## Dry Runs
Use `max_rows` to test with a subset before processing everything. Inline JSON requests can include up to 1,000 items; for larger datasets, use the multipart upload pattern from the reference.
```python theme={null}
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": sample_items, # Up to 1,000 inline items
"output_schema": schema,
"max_rows": 10 # Only process first 10
}
)
# Later, process the rest using source_activity_id
job_id = response.json()["job_id"]
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"source_activity_id": job_id, # Resume from previous job
"output_schema": schema
}
)
```
***
## Full Python Client
```python theme={null}
import requests
import time
class RastroClient:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://catalogapi.rastro.ai/api"
def _headers(self):
return {"Authorization": f"Bearer {self.api_key}"}
def enrich(self, items, output_schema, speed="deep", **kwargs):
response = requests.post(
f"{self.base_url}/public/enrich",
headers=self._headers(),
json={
"items": items,
"output_schema": output_schema,
"speed": speed,
**kwargs
}
)
return response.json()
def poll(self, job_id, page=1, page_size=1000):
"""Poll job status. Returns partial results while running."""
return requests.get(
f"{self.base_url}/public/enrich/{job_id}",
headers=self._headers(),
params={"page": page, "page_size": page_size}
).json()
def get_all_results(self, job_id, page_size=100):
"""Paginate through all available results."""
all_results = []
page = 1
while True:
resp = self.poll(job_id, page=page, page_size=page_size)
all_results.extend(resp["results"])
if page >= resp.get("total_pages", 1):
break
page += 1
return all_results, resp
def enrich_async(self, items, output_schema, speed="deep", poll_interval=5, **kwargs):
# Start job
result = self.enrich(items, output_schema, speed, async_mode=True, **kwargs)
job_id = result["job_id"]
print(f"Started job: {job_id}")
# Poll until complete — partial results available while running
while True:
status = self.poll(job_id, page_size=1)
completed = status.get("completed_items", 0)
total = status.get("total_items", "?")
if status["status"] != "running":
break
print(f"Progress: {completed}/{total} items ready")
time.sleep(poll_interval)
# Fetch all results with pagination
all_results, final = self.get_all_results(job_id)
final["results"] = all_results
return final
# Example usage
client = RastroClient("YOUR_API_KEY")
# Synchronous enrichment
result = client.enrich(
items=[{"part_number": "6205-2RS"}],
output_schema=[
{"name": "bore_diameter", "type": "string", "description": "Inner diameter"},
{"name": "manufacturer", "type": "string", "description": "Brand name"}
]
)
for item in result["results"]:
data = item["after_data"]
sources = data.get("sources", {})
print(f"\n{item['original_data']}:")
for field, value in data.items():
if field in ("sources", "source_explanations"):
continue
print(f" {field}: {value}")
if field in sources:
print(f" Sources: {sources[field]}")
```
***
## Full TypeScript Client
```typescript theme={null}
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://catalogapi.rastro.ai/api";
interface OutputField {
name: string;
type: "string" | "number" | "integer" | "boolean" | "array";
description: string;
unit?: string;
enum?: string[];
}
interface EnrichOptions {
speed?: "fast" | "medium" | "deep" | "slow" | "ultra_slow" | "cheap";
async_mode?: boolean;
allowed_domains?: string[];
blocked_domains?: string[];
web_search?: boolean;
predict_taxonomy?: boolean;
predict_taxonomy_attributes?: boolean;
taxonomy?: object;
template_id?: string;
catalog_id?: string;
}
async function enrich(
items: object[],
outputSchema: OutputField[],
options: EnrichOptions = {}
) {
const response = await fetch(`${BASE_URL}/public/enrich`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
items,
output_schema: outputSchema,
...options
})
});
return response.json();
}
async function poll(jobId: string, page = 1, pageSize = 1000) {
const response = await fetch(
`${BASE_URL}/public/enrich/${jobId}?page=${page}&page_size=${pageSize}`,
{ headers: { "Authorization": `Bearer ${API_KEY}` } }
);
return response.json();
}
async function getAllResults(jobId: string, pageSize = 100) {
const allResults: any[] = [];
let page = 1;
let lastResp: any;
while (true) {
lastResp = await poll(jobId, page, pageSize);
allResults.push(...lastResp.results);
if (page >= (lastResp.total_pages || 1)) break;
page++;
}
return { results: allResults, ...lastResp };
}
async function enrichAsync(
items: object[],
outputSchema: OutputField[],
options: EnrichOptions = {},
pollInterval = 5000
) {
const result = await enrich(items, outputSchema, { ...options, async_mode: true });
const jobId = result.job_id;
console.log(`Started job: ${jobId}`);
// Poll until complete — partial results available while running
while (true) {
const status = await poll(jobId, 1, 1);
if (status.status !== "running") break;
console.log(`Progress: ${status.completed_items || 0}/${status.total_items || "?"} items ready`);
await new Promise(r => setTimeout(r, pollInterval));
}
// Fetch all results with pagination
return getAllResults(jobId);
}
// Example usage
const result = await enrich(
[{ part_number: "6205-2RS" }],
[
{ name: "bore_diameter", type: "string", description: "Inner diameter" },
{ name: "manufacturer", type: "string", description: "Brand name" }
],
{ speed: "deep" }
);
for (const item of result.results) {
const { sources, source_explanations, ...fields } = item.after_data;
console.log(`\n${JSON.stringify(item.original_data)}:`);
for (const [field, value] of Object.entries(fields)) {
console.log(` ${field}: ${value}`);
if (sources?.[field]) {
console.log(` Sources: ${sources[field].join(", ")}`);
}
}
}
```
# Enrich Quickstart
Source: https://docs.rastro.ai/enrich/quickstart
Enrich your first product in under 2 minutes
## 1. Get your API key
[dashboard.rastro.ai/settings/api-keys](https://dashboard.rastro.ai/settings/api-keys) → Create New Key → Copy it.
## 2. Enrich your first item
```python Python theme={null}
import requests
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"},
{"name": "manufacturer", "type": "string", "description": "Brand or manufacturer name"}
],
"speed": "fast"
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"},
{"name": "manufacturer", "type": "string", "description": "Brand or manufacturer name"}
],
"speed": "fast"
}'
```
```typescript TypeScript theme={null}
const response = await fetch("https://catalogapi.rastro.ai/api/public/enrich", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
items: [{ part_number: "6205-2RS", name: "Deep Groove Ball Bearing" }],
output_schema: [
{ name: "bore_diameter", type: "string", description: "Inner diameter in mm" },
{ name: "outer_diameter", type: "string", description: "Outer diameter in mm" },
{ name: "manufacturer", type: "string", description: "Brand or manufacturer name" }
],
speed: "fast"
})
});
console.log(await response.json());
```
Use `"speed": "fast"` for quick testing. For production, omit `speed` or use `"deep"` for thorough results; `"slow"` is accepted as an alias.
## 3. Response
```json theme={null}
{
"job_id": "abc123-def456-...",
"results": [{
"original_data": {"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
"after_data": {
"part_number": "6205-2RS",
"name": "Deep Groove Ball Bearing",
"bore_diameter": "25 mm",
"outer_diameter": "52 mm",
"manufacturer": "SKF",
"sources": {
"bore_diameter": ["https://skf.com/products/bearings/6205-2RS"],
"outer_diameter": ["https://skf.com/products/bearings/6205-2RS"],
"manufacturer": ["https://skf.com/products/bearings/6205-2RS"]
}
},
"all_sources": ["https://skf.com/products/bearings/6205-2RS"]
}],
"total_items": 1,
"successful": 1,
"credits_used": 1,
"status": "completed"
}
```
Each result includes `all_sources` — the URLs used to find the data.
***
## What's Next
All parameters and response fields
Async mode, domain filtering, taxonomy, quality scoring
# Enrich API Reference
Source: https://docs.rastro.ai/enrich/reference
Complete API documentation for the Enrich endpoint
## POST /public/enrich
Enrich items with AI-powered web research. Every extracted field includes source URLs.
```python Python theme={null}
import requests
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"}
]
}
)
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"}
]
}'
```
```typescript TypeScript theme={null}
const response = await fetch("https://catalogapi.rastro.ai/api/public/enrich", {
method: "POST",
headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
items: [{ part_number: "6205-2RS", name: "Deep Groove Ball Bearing" }],
output_schema: [
{ name: "bore_diameter", type: "string", description: "Inner diameter in mm" },
{ name: "outer_diameter", type: "string", description: "Outer diameter in mm" }
]
})
});
```
***
## Request Parameters
### Core Parameters
| Parameter | Type | Required | Default | Description |
| ------------------ | ------ | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `items` | array | Conditional | `[]` | JSON objects to enrich. Required unless you provide `source_activity_id`, `reextract_from_activity_id`, `source_catalog_id`, or multipart `items_file` |
| `output_schema` | array | Conditional | `[]` | Fields to extract. Required unless supplied by `catalog_id`, `template_id`, or a source activity/catalog |
| `prompt` | string | No | `""` | Search prompt guiding web research |
| `template_id` | string | No | - | Apply an organization-scoped enrichment template. Template defaults fill omitted request fields and template safety controls remain enforced |
| `attribute_sets` | object | No | - | Registry for repeated requested-attribute definitions. Items reference entries with `attribute_set_id` |
| `source_hint_sets` | object | No | - | Registry for repeated source hints. Items reference entries with `source_hint_set_id` |
### Search Options
| Parameter | Type | Default | Description |
| ----------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `speed` | string | `"deep"` | `"fast"` (quick), `"medium"` (balanced), `"deep"`/`"slow"` (thorough), `"ultra_slow"` (maximum reasoning), `"cheap"` (lowest cost) |
| `allowed_domains` | array | any | Restrict sources to these domains only |
| `blocked_domains` | array | - | Exclude these domains from search and URL fetching |
| `web_search` | boolean | `true` | Set `false` to skip web search and use input data directly |
### Taxonomy & Quality
| Parameter | Type | Default | Description |
| ----------------------------- | ------- | ------- | -------------------------------------------------------------------- |
| `taxonomy` | object | - | Taxonomy definition for category prediction |
| `predict_taxonomy` | boolean | `false` | Predict category for each item |
| `predict_taxonomy_attributes` | boolean | `true` | Predict category-specific `taxonomy_attributes` after classification |
| `validate_semantics` | boolean | `false` | AI validates values match field descriptions |
| `low_confidence` | object | - | Return plausible but weak candidates outside canonical output |
### Job Control
| Parameter | Type | Default | Description |
| ----------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------- |
| `catalog_id` | string | - | Catalog ID from the dashboard — automatically applies its schema, taxonomy, and settings |
| `source_catalog_id` | string | - | Read all items from this catalog as enrichment input |
| `async_mode` | boolean | `false` | Return immediately with `job_id` |
| `max_rows` | integer | - | Process only first N items (dry run) |
| `source_activity_id` | string | - | Resume from previous job to process remaining items |
| `reextract_from_activity_id` | string | - | Re-extract from stored research in a previous activity, with optional prompt/schema/feedback changes |
| `include_source_explanations` | boolean | `true` | Add explanation for each field value. Public jobs keep this enabled |
| `include_service_usage` | boolean | `false` | Include detailed cost, token, and call metrics when the job completes |
| `capture_source_artifacts` | boolean | `false` | Preserve durable source copies for audit trails |
| `host_media` | boolean | `false` | Copy returned image/document URLs into Rastro-hosted storage |
| `strict_hosted_media` | boolean | `false` | Clear media/document outputs unless hosting succeeds |
| `webhook_url` | string | - | HTTPS URL to receive a POST when the job completes (see [Webhooks](#webhooks)) |
***
## Large Jobs And File Uploads
Inline JSON requests are capped at 1000 items and 10 MB. For larger jobs, send `multipart/form-data` with a small `request_json` field and exactly one `items_file` upload.
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F 'request_json={"output_schema":[{"name":"manufacturer","type":"string","description":"Brand or manufacturer"}],"speed":"deep","async_mode":true}' \
-F "items_file=@items.json;type=application/json"
```
`items_file` must be either a JSON array of item objects or an object with an `items` array. Do not send `items_storage_url`; that field is internal.
***
## Output Schema
Define fields to extract. Each field needs a `name`, `type`, and `description`.
```json theme={null}
{
"output_schema": [
{"name": "material", "type": "string", "description": "Material composition"},
{"name": "weight_kg", "type": "number", "description": "Weight in kilograms", "unit": "kg"},
{"name": "certifications", "type": "array", "description": "Safety certifications"}
]
}
```
### Field Options
| Option | Type | Description |
| -------------------- | ------- | ----------------------------------------------------------------- |
| `name` | string | Field name (required) |
| `type` | string | `string`, `number`, `integer`, `boolean`, `array` |
| `description` | string | What to extract (required) |
| `unit` | string | Units for numbers (e.g., `"kg"`, `"mm"`) |
| `enum` | array | Constrain to specific values |
| `sample_values` | array | Example values to guide extraction |
| `array_element_type` | string | For arrays: `string`, `number`, `image_url` |
| `items_enum` | array | For array-type fields, constrains array items to specific values |
| `required` | boolean | Marks whether this field is required in the output |
| `merge` | boolean | Merge enriched values with existing values instead of overwriting |
***
## Response
```json theme={null}
{
"job_id": "abc123-def456-...",
"results": [{
"original_data": {"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
"after_data": {
"part_number": "6205-2RS",
"name": "Deep Groove Ball Bearing",
"bore_diameter": "25 mm",
"outer_diameter": "52 mm",
"sources": {
"bore_diameter": ["https://skf.com/products/bearings/6205-2RS"],
"outer_diameter": ["https://skf.com/products/bearings/6205-2RS"]
},
"source_explanations": {
"bore_diameter": "Found on manufacturer product page",
"outer_diameter": "Found on manufacturer product page"
}
},
"all_sources": ["https://skf.com/products/bearings/6205-2RS"]
}],
"total_items": 1,
"successful": 1,
"credits_used": 1,
"status": "completed"
}
```
### Response Fields
| Field | Type | Description |
| ------------------------ | ------- | ----------------------------------------------------------------- |
| `job_id` | string | Job ID for tracking |
| `results` | array | Enriched items (empty if `async_mode=true`) |
| `total_items` | integer | Total items submitted |
| `total_rows` | integer | Total items before `max_rows` limit |
| `successful` | integer | Successfully enriched items |
| `credits_used` | integer | Credits used (1 per item) |
| `status` | string | `"running"`, `"completed"`, or `"failed"` |
| `completed_items` | integer | Items with results available so far. Present on polling responses |
| `pricing_estimates` | object | Pricing estimate details when available |
| `knowledge_base_sources` | object | Source IDs to hosted document URLs when a knowledge base is used |
### Result Item Fields
| Field | Type | Description |
| --------------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `original_data` | object | Input data |
| `after_data` | object | Complete enriched record. Contains original fields, enriched values, `sources` (per-field URL citations), and `source_explanations` (per-field derivation explanations) |
| `all_sources` | array | Deduplicated list of all URLs used across all fields |
| `error` | string | Error message if failed |
| `category_id` | string | Predicted category (if taxonomy enabled) |
| `category_path` | string | Full path like `"Bearings > Ball Bearings"` |
| `taxonomy_attributes` | object | Category-specific attributes |
| `taxonomy_attribute_explanations` | object | Per-attribute explanations of how taxonomy values were derived |
| `review_info` | object | AI reasoning and flags (see below) |
| `low_confidence_candidates` | array | Plausible but non-canonical candidates when enabled |
| `low_confidence_fields` | array | Fields that should be highlighted as low confidence |
***
## GET /public/enrich/
Poll enrichment job status and retrieve results. Results are returned **progressively** — partial results are available while the job is still running, as each batch of items completes processing.
### Query Parameters
| Parameter | Type | Default | Description |
| ----------- | ------- | ------- | ----------------------- |
| `page` | integer | `1` | Page number (1-indexed) |
| `page_size` | integer | `1000` | Results per page |
```python Python theme={null}
# Basic poll
response = requests.get(
f"https://catalogapi.rastro.ai/api/public/enrich/{job_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
# With pagination
response = requests.get(
f"https://catalogapi.rastro.ai/api/public/enrich/{job_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"page": 1, "page_size": 50}
)
```
```bash cURL theme={null}
# Basic poll
curl "https://catalogapi.rastro.ai/api/public/enrich/{job_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
# With pagination
curl "https://catalogapi.rastro.ai/api/public/enrich/{job_id}?page=1&page_size=50" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
// Basic poll
const response = await fetch(`https://catalogapi.rastro.ai/api/public/enrich/${jobId}`, {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
// With pagination
const response = await fetch(
`https://catalogapi.rastro.ai/api/public/enrich/${jobId}?page=1&page_size=50`,
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
```
### Poll Response
```json theme={null}
{
"job_id": "abc123-def456-...",
"status": "running",
"results": [
{
"original_data": {"part_number": "6205-2RS"},
"after_data": {
"part_number": "6205-2RS",
"bore_diameter": "25 mm",
"sources": {
"bore_diameter": ["https://skf.com/products/bearings/6205-2RS"]
}
},
"all_sources": ["https://skf.com/products/bearings/6205-2RS"]
}
],
"total_items": 500,
"completed_items": 200,
"successful": 0,
"credits_used": 0,
"page": 1,
"page_size": 50,
"total_pages": 4
}
```
### Poll Response Fields
| Field | Type | Description |
| ----------------- | ------- | ----------------------------------------------------------- |
| `job_id` | string | Job ID |
| `status` | string | Current status (see below) |
| `results` | array | Enriched items for the current page |
| `total_items` | integer | Total items submitted |
| `completed_items` | integer | Items with results available so far (grows while `running`) |
| `successful` | integer | Finalized success count (set when job finishes) |
| `credits_used` | integer | Credits charged (`0` while `running`) |
| `page` | integer | Current page number |
| `page_size` | integer | Results per page |
| `total_pages` | integer | Total pages based on `completed_items` |
| `service_usage` | object | Cost details (only present when job finishes) |
While `status` is `running`, `completed_items` reflects how many items have finished so far. Use `completed_items` and `total_items` to show progress. `credits_used` remains `0` until the job finishes.
### Status Values
| Status | Description | Results available? |
| ----------- | ------------------------------- | -------------------------------------------- |
| `running` | Job is still processing | Yes — partial results from completed batches |
| `completed` | Job finished, all results ready | Yes — all results |
| `failed` | Job failed or was cancelled | Yes — partial results from completed batches |
Cancelled jobs are reported as `failed`. Partial results and credits for completed items are preserved.
***
## Source Citations
Each result includes `all_sources` — the URLs used during enrichment.
```json theme={null}
{
"all_sources": [
"https://skf.com/products/bearings/6205-2RS",
"https://mcmaster.com/6205-2RS"
]
}
```
When `web_search` is `false`, the AI uses only the input data (`INPUT_DATA`) to derive field values.
***
## AI Transparency
Every result includes `review_info` with AI reasoning:
```json theme={null}
{
"review_info": {
"reasoning": "Found specs on manufacturer website, cross-referenced with distributor",
"flags": ["verify_tolerance_class"],
"field_issues": [
{"field": "tolerance", "severity": "warning", "message": "Value not found in sources"}
],
"flag_record": true
}
}
```
| Field | Type | Description |
| -------------- | ------- | ------------------------------------------------------------------------------------------ |
| `reasoning` | string | AI explanation of how values were extracted |
| `flags` | array | General flags or warnings about the extraction |
| `field_issues` | array | Per-field issues with `field`, `severity` (error/warning/info), `message`, and `validator` |
| `flag_record` | boolean | Whether this record needs human attention |
***
## Webhooks
Instead of polling `GET /public/enrich/{job_id}`, you can provide a `webhook_url` to receive a POST when the job finishes. The URL **must use HTTPS**.
```json theme={null}
{
"items": [...],
"output_schema": [...],
"async_mode": true,
"webhook_url": "https://yourserver.com/hooks/enrich"
}
```
### Webhook Payload
When the job completes, we POST a JSON body to your URL:
```json theme={null}
{
"job_id": "abc123-def456-...",
"status": "completed",
"successful": 150,
"failed": 5,
"total": 155,
"credits_used": 150
}
```
| Field | Type | Description |
| -------------- | ------- | ------------------------------------------------------------ |
| `job_id` | string | Use with `GET /public/enrich/{job_id}` to fetch full results |
| `status` | string | Always `"completed"` |
| `successful` | integer | Items enriched successfully |
| `failed` | integer | Items that failed |
| `total` | integer | `successful + failed` |
| `credits_used` | integer | Credits charged |
Webhook delivery is best-effort with a single attempt and a 30-second timeout. If delivery fails the job is unaffected — use polling as a fallback.
***
## Job Management
### GET /public/enrich/jobs
List enrichment jobs for your organization, with optional status filtering and pagination.
#### Query Parameters
| Parameter | Type | Default | Description |
| --------- | ------- | ------- | --------------------------------------------------------------- |
| `status` | string | (all) | Filter by status: `running`, `completed`, `failed`, `cancelled` |
| `limit` | integer | `20` | Results per page (1–100) |
| `offset` | integer | `0` | Pagination offset |
```python Python theme={null}
import requests
# List running jobs
response = requests.get(
"https://catalogapi.rastro.ai/api/public/enrich/jobs",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"status": "running"}
)
jobs = response.json()
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/enrich/jobs?status=running" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
const response = await fetch(
"https://catalogapi.rastro.ai/api/public/enrich/jobs?status=running",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
```
#### Response
```json theme={null}
{
"jobs": [
{
"job_id": "abc123-def456-...",
"status": "running",
"total_items": 500,
"completed_items": 200,
"failed_items": 3,
"created_at": "2026-02-11T10:00:00+00:00",
"started_at": "2026-02-11T10:00:01+00:00",
"finished_at": null
}
],
"total": 12
}
```
| Field | Type | Description |
| ------- | ------- | ------------------------------ |
| `jobs` | array | List of job summaries |
| `total` | integer | Total jobs matching the filter |
Each job object:
| Field | Type | Description |
| ----------------- | ------- | ------------------------------------------------ |
| `job_id` | string | Job ID (use with other endpoints) |
| `status` | string | `running`, `completed`, `failed`, or `cancelled` |
| `total_items` | integer | Items submitted |
| `completed_items` | integer | Items processed so far |
| `failed_items` | integer | Items that failed |
| `created_at` | string | ISO 8601 timestamp |
| `started_at` | string | ISO 8601 timestamp (null if not yet started) |
| `finished_at` | string | ISO 8601 timestamp (null if still running) |
***
### POST /public/enrich//cancel
Cancel a running enrichment job. Items already processed are kept and credits are charged only for completed items.
```python Python theme={null}
import requests
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/enrich/{job_id}/cancel",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
result = response.json()
print(f"Cancelled — {result['completed_items']} items kept, {result['credits_used']} credits charged")
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich/{job_id}/cancel" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
const response = await fetch(
`https://catalogapi.rastro.ai/api/public/enrich/${jobId}/cancel`,
{ method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const result = await response.json();
```
#### Response
```json theme={null}
{
"success": true,
"message": "Activity abc123-def456 cancelled",
"completed_items": 200,
"credits_used": 200
}
```
| Field | Type | Description |
| ----------------- | ------- | --------------------------------------- |
| `success` | boolean | Whether cancellation succeeded |
| `message` | string | Human-readable status |
| `completed_items` | integer | Items that finished before cancellation |
| `credits_used` | integer | Credits charged for completed items |
Cancellation is intended for active jobs. Jobs that are already cancelled return a successful cancelled response; completed jobs may return an error.
***
### POST /public/enrich//to-catalog
Create a new catalog from an enrichment run.
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich/{job_id}/to-catalog" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Enriched Products"}'
```
Use this when you ran enrichment first and want to persist the finished results as a new catalog.
***
## Templates, Inference, And Judging
| Endpoint | Description |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| `GET /public/enrichment-templates` | List enabled enrichment templates available to your organization |
| `GET /public/enrichment-templates/{template_id}` | Get a template. Supports `include_taxonomy=true` and `taxonomy_format=expanded` or `taxonomy_format=compact` |
| `POST /public/infer-enrichment` | Infer catalog schema, taxonomy, and web enrichment fields from sample items |
| `POST /public/infer-schema` | Upload a CSV/Excel file and infer schema plus enrichment config |
| `POST /public/judge` | Judge rows against a catalog schema or inline schema. Supports per-row image URLs |
Example inference request:
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/infer-enrichment" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"Industrial bearings catalog","items":[{"sku":"6205-2RS","name":"Deep Groove Ball Bearing"}]}'
```
Example judge request:
```bash theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/judge" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"catalog_id":"cat_123","rows":[{"sku":"A1","title":"Product A"}],"max_rows":50}'
```
# Flows Examples
Source: https://docs.rastro.ai/flows/examples
Practical examples for executing flows
## Basic Flow Execution
```python Python theme={null}
import requests
API_KEY = "YOUR_API_KEY"
FLOW_ID = "your-flow-id"
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/workflows/{FLOW_ID}/execute",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"input": [
{"sku": "A1", "title": "Product A", "price": 29.99},
{"sku": "A2", "title": "Product B", "price": 39.99},
{"sku": "A3", "title": "Product C", "price": 49.99}
]
}
)
result = response.json()
print(f"Run ID: {result['workflow_run_id']}")
```
```typescript TypeScript theme={null}
const API_KEY = "YOUR_API_KEY";
const FLOW_ID = "your-flow-id";
const response = await fetch(`https://catalogapi.rastro.ai/api/public/workflows/${FLOW_ID}/execute`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
input: [
{ sku: "A1", title: "Product A", price: 29.99 },
{ sku: "A2", title: "Product B", price: 39.99 },
{ sku: "A3", title: "Product C", price: 49.99 }
]
})
});
const result = await response.json();
console.log(`Run ID: ${result.workflow_run_id}`);
```
***
## Polling for Results
```python Python theme={null}
import requests
import time
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://catalogapi.rastro.ai/api"
FLOW_ID = "your-flow-id"
def run_flow(items):
# Start the flow
response = requests.post(
f"{BASE_URL}/public/workflows/{FLOW_ID}/execute",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"input": items}
)
run_id = response.json()["workflow_run_id"]
print(f"Flow started: {run_id}")
# Poll for results
while True:
status = requests.get(
f"{BASE_URL}/public/workflows/runs/{run_id}",
headers={"Authorization": f"Bearer {API_KEY}"}
).json()
if status["status"] == "completed":
return status["results"]["data"]
elif status["status"] == "failed":
raise Exception("Flow failed")
print(f"Progress: {status.get('progress', 0) * 100:.0f}%")
time.sleep(5)
# Example usage
items = [
{"sku": "A1", "title": "Product A"},
{"sku": "A2", "title": "Product B"},
{"sku": "A3", "title": "Product C"}
]
results = run_flow(items)
for item in results:
print(item)
```
```typescript TypeScript theme={null}
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://catalogapi.rastro.ai/api";
const FLOW_ID = "your-flow-id";
async function runFlow(items: object[]) {
// Start the flow
const startResponse = await fetch(`${BASE_URL}/public/workflows/${FLOW_ID}/execute`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ input: items })
});
const { workflow_run_id } = await startResponse.json();
console.log(`Flow started: ${workflow_run_id}`);
// Poll for results
while (true) {
const statusResponse = await fetch(`${BASE_URL}/public/workflows/runs/${workflow_run_id}`, {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const status = await statusResponse.json();
if (status.status === "completed") {
return status.results.data;
} else if (status.status === "failed") {
throw new Error("Flow failed");
}
console.log(`Progress: ${(status.progress || 0) * 100}%`);
await new Promise(r => setTimeout(r, 5000));
}
}
// Example usage
const items = [
{ sku: "A1", title: "Product A" },
{ sku: "A2", title: "Product B" }
];
const results = await runFlow(items);
console.log(results);
```
***
## Full Python Client
```python theme={null}
import requests
import time
class FlowsClient:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://catalogapi.rastro.ai/api"
def _headers(self):
return {"Authorization": f"Bearer {self.api_key}"}
def execute(self, flow_id, items):
"""Start a flow execution."""
response = requests.post(
f"{self.base_url}/public/workflows/{flow_id}/execute",
headers=self._headers(),
json={"input": items}
)
return response.json()
def get_status(self, run_id):
"""Get the status of a flow run."""
response = requests.get(
f"{self.base_url}/public/workflows/runs/{run_id}",
headers=self._headers()
)
return response.json()
def execute_and_wait(self, flow_id, items, poll_interval=5, on_progress=None):
"""Execute a flow and wait for results."""
result = self.execute(flow_id, items)
run_id = result["workflow_run_id"]
while True:
status = self.get_status(run_id)
if status["status"] == "completed":
return status["results"]["data"]
elif status["status"] == "failed":
raise Exception(f"Flow failed: {status.get('error', 'Unknown error')}")
if on_progress:
on_progress(status.get("progress", 0))
time.sleep(poll_interval)
# Example usage
client = FlowsClient("YOUR_API_KEY")
# Execute and wait
results = client.execute_and_wait(
flow_id="your-flow-id",
items=[
{"sku": "A1", "title": "Product A"},
{"sku": "A2", "title": "Product B"}
],
on_progress=lambda p: print(f"Progress: {p * 100:.0f}%")
)
for item in results:
print(item)
```
***
## Full TypeScript Client
```typescript theme={null}
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://catalogapi.rastro.ai/api";
class FlowsClient {
private apiKey: string;
private baseUrl: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
this.baseUrl = BASE_URL;
}
private headers() {
return {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json"
};
}
async execute(flowId: string, items: object[]) {
const response = await fetch(`${this.baseUrl}/public/workflows/${flowId}/execute`, {
method: "POST",
headers: this.headers(),
body: JSON.stringify({ input: items })
});
return response.json();
}
async getStatus(runId: string) {
const response = await fetch(`${this.baseUrl}/public/workflows/runs/${runId}`, {
headers: { "Authorization": `Bearer ${this.apiKey}` }
});
return response.json();
}
async executeAndWait(
flowId: string,
items: object[],
pollInterval = 5000,
onProgress?: (progress: number) => void
) {
const result = await this.execute(flowId, items);
const runId = result.workflow_run_id;
while (true) {
const status = await this.getStatus(runId);
if (status.status === "completed") {
return status.results.data;
} else if (status.status === "failed") {
throw new Error(`Flow failed: ${status.error || "Unknown error"}`);
}
if (onProgress) {
onProgress(status.progress || 0);
}
await new Promise(r => setTimeout(r, pollInterval));
}
}
}
// Example usage
const client = new FlowsClient("YOUR_API_KEY");
const results = await client.executeAndWait(
"your-flow-id",
[
{ sku: "A1", title: "Product A" },
{ sku: "A2", title: "Product B" }
],
5000,
(p) => console.log(`Progress: ${p * 100}%`)
);
console.log(results);
```
# Flows Quickstart
Source: https://docs.rastro.ai/flows/quickstart
Create and execute visual data pipelines
Flows are reusable data pipelines you build visually in the dashboard, then execute via API or UI. Perfect for recurring enrichment tasks.
## 1. Create a flow from a template
1. Go to [dashboard.rastro.ai/flows](https://dashboard.rastro.ai/flows)
2. Click **Templates**
3. Select **Competitor Pricing** (or another template)
4. Upload your CSV
5. Click **Run**
That's it — your flow will process each row and enrich it with the configured data.
## 2. Execute via API
Once you have a flow, you can trigger it programmatically.
Get your Flow ID from the URL: `dashboard.rastro.ai/flows/{FLOW_ID}/edit`
```python Python theme={null}
import requests
FLOW_ID = "your-flow-id"
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/workflows/{FLOW_ID}/execute",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"input": [
{"sku": "A1", "title": "Product A"},
{"sku": "A2", "title": "Product B"}
]
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/workflows/{FLOW_ID}/execute" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": [
{"sku": "A1", "title": "Product A"},
{"sku": "A2", "title": "Product B"}
]
}'
```
```typescript TypeScript theme={null}
const FLOW_ID = "your-flow-id";
const response = await fetch(`https://catalogapi.rastro.ai/api/public/workflows/${FLOW_ID}/execute`, {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
input: [
{ sku: "A1", title: "Product A" },
{ sku: "A2", title: "Product B" }
]
})
});
console.log(await response.json());
```
**Response:**
```json theme={null}
{
"workflow_run_id": "run_abc123",
"status": "running"
}
```
## 3. Check status and get results
```python Python theme={null}
response = requests.get(
"https://catalogapi.rastro.ai/api/public/workflows/runs/run_abc123",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
print(response.json())
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/workflows/runs/run_abc123" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
const response = await fetch("https://catalogapi.rastro.ai/api/public/workflows/runs/run_abc123", {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
console.log(await response.json());
```
**Response (completed):**
```json theme={null}
{
"status": "completed",
"results": {
"data": [
{"sku": "A1", "title": "Product A", "enriched_field": "..."},
{"sku": "A2", "title": "Product B", "enriched_field": "..."}
]
}
}
```
***
## Flows vs Enrich API
| Feature | Enrich API | Flows |
| ------------- | ------------------ | ------------------------- |
| Setup | None | Build in dashboard |
| Best for | One-off enrichment | Recurring pipelines |
| Customization | Prompt + schema | Full visual editor |
| Processing | Sync or async | Async with status polling |
***
## What's Next
All endpoints and parameters
Python and TypeScript examples
# Flows API Reference
Source: https://docs.rastro.ai/flows/reference
Complete API documentation for Flows endpoints
## GET /public/workflows
List available workflows for your organization.
```python Python theme={null}
response = requests.get(
"https://catalogapi.rastro.ai/api/public/workflows",
headers={"Authorization": "Bearer YOUR_API_KEY"},
params={"limit": 50, "offset": 0}
)
print(response.json())
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/workflows?limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"
```
### Query Parameters
| Parameter | Type | Default | Description |
| --------- | ------- | ------- | ------------------------------------------- |
| `search` | string | — | Case-insensitive search on name/description |
| `limit` | integer | 50 | Items per page (1–100) |
| `offset` | integer | 0 | Items to skip |
### Response
```json theme={null}
{
"workflows": [
{"id": "wf_123", "name": "Enrich Products", "description": "..."}
],
"total": 5
}
```
***
## POST /public/workflows//execute
Start a flow execution with input data.
```python Python theme={null}
import requests
FLOW_ID = "your-flow-id"
response = requests.post(
f"https://catalogapi.rastro.ai/api/public/workflows/{FLOW_ID}/execute",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"input": [
{"sku": "A1", "title": "Product A"}
]
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/workflows/{workflow_id}/execute" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": [
{"sku": "A1", "title": "Product A"}
],
"cancel_existing": false
}'
```
```typescript TypeScript theme={null}
const response = await fetch(`https://catalogapi.rastro.ai/api/public/workflows/${workflowId}/execute`, {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
input: [{ sku: "A1", title: "Product A" }]
})
});
```
### Request Parameters
| Parameter | Type | Required | Description |
| ----------------- | ------- | -------- | --------------------------------------------------------------------------------- |
| `input` | array | Yes | Array of objects to process |
| `cancel_existing` | boolean | No | Cancel currently running executions for this workflow before starting the new one |
### Response
```json theme={null}
{
"workflow_run_id": "run_abc123",
"workflow_id": "wf_123",
"workflow_name": "Enrich Products",
"status": "running",
"created_at": "2026-06-29T12:00:00Z",
"poll_url": "/api/public/workflows/runs/run_abc123",
"workflow_url": "https://dashboard.rastro.ai/flows/wf_123/edit",
"total_nodes": 3,
"message": "Workflow execution started"
}
```
| Field | Type | Description |
| ----------------- | ------- | --------------------------------- |
| `workflow_run_id` | string | Run ID for status polling |
| `workflow_id` | string | Workflow that is executing |
| `workflow_name` | string | Workflow display name |
| `status` | string | `"running"` |
| `poll_url` | string | API URL for polling |
| `workflow_url` | string | Dashboard URL for the flow editor |
| `total_nodes` | integer | Number of nodes in the run |
| `message` | string | Human-readable status |
***
## GET /public/workflows/runs/
Check the status of a flow run and retrieve results.
```python Python theme={null}
response = requests.get(
f"https://catalogapi.rastro.ai/api/public/workflows/runs/{workflow_run_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
print(response.json())
```
```bash cURL theme={null}
curl "https://catalogapi.rastro.ai/api/public/workflows/runs/{workflow_run_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
```
```typescript TypeScript theme={null}
const response = await fetch(`https://catalogapi.rastro.ai/api/public/workflows/runs/${workflowRunId}`, {
headers: { "Authorization": "Bearer YOUR_API_KEY" }
});
console.log(await response.json());
```
### Query Parameters
| Parameter | Type | Default | Description |
| ----------- | ------- | ------- | ----------------------------- |
| `page` | integer | `1` | Page number for final results |
| `page_size` | integer | `50` | Results per page |
### Response (running)
```json theme={null}
{
"workflow_run_id": "run_abc123",
"workflow_id": "wf_123",
"workflow_name": "Enrich Products",
"workflow_url": "https://dashboard.rastro.ai/flows/wf_123/edit",
"status": "running",
"progress": 0.5,
"current_step": "processing",
"message": "Processing records",
"created_at": "2026-06-29T12:00:00Z",
"updated_at": "2026-06-29T12:01:00Z",
"completed_at": null,
"updates": ["Processing record 25/50"],
"results": null,
"node_results": null
}
```
### Response (completed)
```json theme={null}
{
"workflow_run_id": "run_abc123",
"workflow_id": "wf_123",
"workflow_name": "Enrich Products",
"workflow_url": "https://dashboard.rastro.ai/flows/wf_123/edit",
"status": "completed",
"progress": 1,
"current_step": "completed",
"message": "Workflow completed",
"created_at": "2026-06-29T12:00:00Z",
"updated_at": "2026-06-29T12:03:00Z",
"completed_at": "2026-06-29T12:03:00Z",
"results": {
"data": [
{"sku": "A1", "title": "Product A", "enriched_field": "..."},
{"sku": "A2", "title": "Product B", "enriched_field": "..."}
],
"total": 2,
"page": 1,
"page_size": 50,
"has_more": false
},
"node_results": {
"node_abc": {
"node_type": "attribute_mapper",
"data": [{"sku": "A1", "enriched_field": "..."}],
"total": 2,
"page": 1,
"page_size": 50,
"has_more": false
}
}
}
```
### Response Fields
| Field | Type | Description |
| -------------- | ------ | ------------------------------------------------------------------------------------ |
| `status` | string | Current status |
| `progress` | number | Progress from 0 to 1 (when running) |
| `current_step` | string | Current workflow step |
| `message` | string | Latest status message |
| `updates` | array | Recent progress update messages |
| `results` | object | Final paginated result set with `data`, `total`, `page`, `page_size`, and `has_more` |
| `node_results` | object | Per-node paginated outputs keyed by node ID |
***
## Status Values
| Status | Description |
| ----------- | -------------------------- |
| `running` | Flow is processing |
| `completed` | Flow finished successfully |
| `failed` | Flow failed |
| `cancelled` | Flow was cancelled |
# Connectors & Integrations
Source: https://docs.rastro.ai/integrations
Enterprise integrations for seamless data flow
Rastro offers enterprise-grade connectors and integrations to fit seamlessly into your existing infrastructure.
## EDI / SFTP File Transfer
Automate data exchange with your trading partners and internal systems. We support standard EDI formats and secure SFTP file transfer for batch processing of catalog data.
* Scheduled imports and exports
* Standard EDI document formats
* Secure SFTP with key-based authentication
* Automatic file processing and validation
## PIM Integrations
White-glove integrations with leading Product Information Management systems. We work directly with your team to ensure seamless bi-directional sync between Rastro and your PIM.
Supported platforms include:
* Akeneo
* Salsify
* inRiver
* Pimcore
* Custom PIM solutions
## Knowledge Base Grounded Enrichment
For enterprise customers, Rastro can enrich your catalog using your own proprietary data sources:
* Internal product databases
* Supplier portals and datasheets
* Historical catalog data
* Custom knowledge bases and document repositories
This ensures enrichment results align with your organization's trusted data sources and business rules.
***
## MCP for Agent Workflows
Rastro supports MCP integrations for Codex and Claude so your team can run catalog operations through AI agents while keeping review/apply in dashboard.
* API-key-only authentication
* No direct DB access required
* Activity-first safety model for bulk edits
See the full setup at [MCP Quickstart](/mcp/quickstart).
***
## Get Started
These features are available for enterprise customers. Contact us for a full exploration of how Rastro can integrate with your systems.
Email [baptiste@rastro.ai](mailto:baptiste@rastro.ai) to discuss your integration needs
# Rastro
Source: https://docs.rastro.ai/introduction
AI-powered catalog enrichment and normalization with enterprise-grade data governance
Transform raw product data into complete, structured catalogs. Every field is validated, sourced, and ready for production. Fully compatible with your existing PIM and ERP schemas.
Enrich your first item in 2 minutes
Full endpoint documentation
***
## Why Rastro?
### Auto-Scaling Infrastructure
Processing 30,000+ SKUs is complex — managing queues, handling failures, rate limiting, and scaling workers. Rastro handles all of this for you. Just send your data and we process it at scale.
### Web or Internal Sources with Full Traceability
Rastro can fetch information for you from anywhere on the internet with no URLs provided, PDFs, images, and any data or links you provide.
Every enriched value includes sources showing where data was found, plus optional explanations of how values were derived. No black boxes—audit any data point back to its origin:
```json theme={null}
{
"after_data": {
"material": "316 Stainless Steel",
"bore_diameter": "25 mm",
"datasheet_url": "https://example.com/spec.pdf",
"sources": {
"material": ["https://mcmaster.com/catalog/91251A"],
"bore_diameter": ["https://mcmaster.com/catalog/91251A"],
"datasheet_url": ["https://mcmaster.com/catalog/91251A"]
},
"source_explanations": {
"material": "Extracted from product title and specifications table",
"bore_diameter": "Extracted from product dimensions section",
"datasheet_url": "Extracted from specifications table on page 2"
}
},
"all_sources": ["https://mcmaster.com/catalog/91251A"]
}
```
Set `"web_search": false` to skip web lookups and only process your input data. Sources will reference `INPUT_DATA` instead of URLs.
### Automatic Error Flagging
AI flags uncertain values, conflicting data, and potential issues for human review. Every result includes reasoning and flags.
```json theme={null}
{
"review_info": {
"reasoning": "Found conflicting specs across sources",
"flags": ["conflicting_data", "verify_dimensions"],
"flag_record": true
}
}
```
### Taxonomy & Category Prediction
Automatically classify items into your taxonomy with attribute extraction. Define your category hierarchy once—Rastro assigns items and extracts category-specific attributes.
```json theme={null}
{
"category_path": "Fasteners > Bolts > Hex Bolts",
"taxonomy_attributes": {
"Thread Size": "M10-1.5",
"Head Type": "Hex",
"Grade": "A2-70"
}
}
```
### Complex Field Types
Define fields with units, enums, arrays, and validation constraints. Rastro extracts structured data matching your exact schema.
```json theme={null}
{
"output_schema": [
{"name": "weight", "type": "number", "unit": "kg"},
{"name": "material", "type": "string", "enum": ["Steel", "Aluminum", "Brass"]},
{"name": "certifications", "type": "array", "items_enum": ["ISO 9001", "CE", "UL"]}
]
}
```
### Row Judging
Judge rows against your catalog schema and quality prompt. Identify incomplete records before they hit production.
```json theme={null}
{
"judgments": [
{
"row_index": 0,
"decision": "review_required",
"reasons": ["Complete specs, missing datasheet URL"],
"field_issues": ["datasheet_url is blank"]
}
]
}
```
***
Web research with citations
Visual data pipelines
Managed product storage
AI product photo editing and video
Agent-driven bulk catalog operations with review safety
# MCP Quickstart
Source: https://docs.rastro.ai/mcp/quickstart
Set up the Rastro MCP server in Claude Code, Claude Desktop, Cursor, or Codex
Set up the Rastro MCP server to run catalog operations from your AI agent. Pull snapshots, transform data locally, stage changes for review, and apply from the dashboard.
No database credentials needed — the MCP server talks only to the Rastro API.
## Prerequisites
* A Rastro account at [dashboard.rastro.ai](https://dashboard.rastro.ai)
* [uv](https://docs.astral.sh/uv/) installed (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
## Authenticate
```bash theme={null}
uvx rastro.ai login
```
This opens [dashboard.rastro.ai](https://dashboard.rastro.ai) in your browser. Log in, and your credentials are saved automatically.
Alternatively, create an API key at [dashboard.rastro.ai/settings/api-keys](https://dashboard.rastro.ai/settings/api-keys) and pass it as `RASTRO_API_KEY` in your client config below.
## Configure your client
```bash theme={null}
claude mcp add rastro -- uvx rastro.ai
```
If using an API key instead of browser login:
```bash theme={null}
claude mcp add rastro \
-e RASTRO_API_KEY=rastro_pk_... \
-- uvx rastro.ai
```
Open your config file:
* **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
If you ran `uvx rastro.ai login` above:
```json theme={null}
{
"mcpServers": {
"rastro": {
"command": "uvx",
"args": ["rastro.ai"]
}
}
}
```
Or with an API key:
```json theme={null}
{
"mcpServers": {
"rastro": {
"command": "uvx",
"args": ["rastro.ai"],
"env": {
"RASTRO_API_KEY": "rastro_pk_..."
}
}
}
}
```
Restart Claude Desktop after saving.
Open **Cursor Settings** (Cmd/Ctrl + Shift + J) → **MCP** tab → **Add new MCP server**.
Or add to `.cursor/mcp.json` in your project root:
```json theme={null}
{
"mcpServers": {
"rastro": {
"command": "uvx",
"args": ["rastro.ai"]
}
}
}
```
If using an API key instead of browser login, add an `"env"` field:
```json theme={null}
"env": {
"RASTRO_API_KEY": "rastro_pk_..."
}
```
Add to `~/.codex/config.toml`:
```toml theme={null}
[mcp_servers.rastro]
command = "uvx"
args = ["rastro.ai"]
```
If using an API key instead of browser login:
```toml theme={null}
[mcp_servers.rastro]
command = "uvx"
args = ["rastro.ai"]
env = { RASTRO_API_KEY = "rastro_pk_..." }
```
## Verify
Ask your agent:
```text theme={null}
List my Rastro catalogs
```
If the server is connected, it will call `catalog_list` and return your catalogs.
## First workflow
Prompt your agent:
```text theme={null}
For catalog , pull a snapshot, update price/cost by my rules,
compute diff, validate, and stage one pending-review activity.
Do not apply automatically.
```
Expected flow:
1. `execution_catalog_snapshot_pull`
2. Local transform (agent-generated Python)
3. `execution_local_diff_compute`
4. `execution_bundle_validate`
5. `catalog_activity_create_transform` (or `execution_catalog_stage_dataset`)
6. Review and apply in the [dashboard](https://dashboard.rastro.ai)
## Safety defaults
* Programmatic approve/apply is blocked — all changes require dashboard review.
* Large change sets are staged into one activity (chunked internally).
* Always review the dashboard diff before applying.
## Next
Full tool reference and troubleshooting
Underlying API endpoints
# MCP Reference
Source: https://docs.rastro.ai/mcp/reference
Rastro MCP tools, access model, and operational guidance
## Access model
Rastro MCP is API-first:
* Auth: `Authorization: Bearer rastro_pk_...`
* Base URL: `https://catalogapi.rastro.ai/api`
* Data access: public API endpoints only
* Database access: not required
This means customers can run MCP in their own Codex/Claude environment without internal DB credentials.
MCP accepts either `RASTRO_API_KEY` or a user bearer token via `RASTRO_ACCESS_TOKEN`.
CLI auth helper: `uv run rastro-mcp login` opens dashboard and returns token to localhost callback.
Standalone package repo: [github.com/Rastro-AI/rastro-mcp](https://github.com/Rastro-AI/rastro-mcp)
## Tool groups
### `catalog_*`
Catalog and activity operations:
* `catalog_list`, `catalog_get`, `catalog_schema_get`, `catalog_taxonomy_get`
* `catalog_items_query`, `catalog_item_get`
* `catalog_activity_list`, `catalog_activity_get`, `catalog_activity_get_staged_changes`
* `catalog_activity_create_transform`
* `catalog_activity_audit` (activity + staged summary)
* `catalog_activity_clear` (reject + cancel stale activities)
* `catalog_snapshot_list`, `catalog_snapshot_create`, `catalog_snapshot_restore`
### `execution_*`
Local file pipeline:
* `execution_catalog_snapshot_pull`
* `execution_local_diff_compute`
* `execution_bundle_validate`
* `execution_catalog_stage_dataset` (one-command diff + stage)
### `service_*`
Specialized services:
* `service_map_to_catalog_schema`
* `service_image_run`, `service_image_status`, `service_image_list`
## Catalog update pattern (recommended)
1. Pull full snapshot with `execution_catalog_snapshot_pull`
2. Transform locally (Python)
3. Compute diff with `execution_local_diff_compute`
4. Validate with `execution_bundle_validate`
5. Stage a single review activity
6. Open dashboard link and review before apply
## Safety behavior
* MCP does not programmatically approve/apply staged changes.
* Deletes are supported and validated in diff/validation pipeline.
* Large staged change sets are chunked internally but kept under one activity.
## Deterministic diffing
`execution_local_diff_compute` supports:
* `key_field` (default `__catalog_item_id`)
* `deterministic_key_fields` for fallback identity matching
* `allow_row_index_fallback` for deterministic row-position fallback
Outputs include:
* `diff_summary`
* `staged_changes_path`
* `diff_details_path`
* `field_change_counts`
* `entity_type_change_counts`
* key diagnostics and sample field-level changes
## API endpoints used by MCP (high level)
* `GET /public/catalogs/{catalog_id}`
* `GET /public/catalogs/{catalog_id}/schema`
* `GET /public/catalogs/{catalog_id}/taxonomy`
* `GET /public/catalogs/{catalog_id}/items`
* `GET /public/catalogs/{catalog_id}/raw-items`
* `GET /public/catalogs/{catalog_id}/activities`
* `POST /public/catalogs/{catalog_id}/activities`
* `POST /public/activities/{activity_id}/staged-changes/append`
* `POST /public/activities/{activity_id}/pending-review`
* `GET /activities/{activity_id}`
* `GET /activities/{activity_id}/staged-changes`
* `GET /activities/{activity_id}/staged-changes/summary`
* `POST /activities/{activity_id}/staged-changes/bulk-review` (reject-only from MCP)
* `POST /activities/{activity_id}/cancel`
## Performance tuning
Optional env vars:
* `RASTRO_MCP_PULL_MAX_CONCURRENCY` (default `8`)
* `RASTRO_MCP_STAGE_BATCH_SIZE` (default `2000`)
* `RASTRO_MCP_STAGE_RETRIES` (default `3`)
## Troubleshooting
### 401 / 403
* Check API key is valid and belongs to the correct organization.
### Snapshot returns fewer rows than expected
* Use raw pulls (`prefer_raw: true`) and verify `total` count in response.
### Diff appears misaligned
* Pass `deterministic_key_fields` explicitly for business identity fields (e.g., `sku`, `product_id`) and re-run diff.
### Too many old staged activities
* Use `catalog_activity_audit` to inspect status and staged counts.
* Use `catalog_activity_clear` to reject/cancel stale activities in bulk.
# Photo Studio Quickstart
Source: https://docs.rastro.ai/photo-studio/quickstart
Edit product photos and generate marketing images with AI
Photo Studio is the AI image editor built into the Rastro dashboard. Select any product in a catalog, describe the change you want in plain language, and generate polished product imagery — lifestyle scenes, clean packshots, marketing shots, even short videos. Results save straight back to the product's image gallery.
Photo Studio works on the products in a **catalog**, so you need one before you start. Create a catalog and add items with product images first — see the [Catalogs Quickstart](/catalogs/quickstart). If you have no catalog yet, you can also create one directly from the catalog picker inside Photo Studio.
## 1. Open Photo Studio and pick a catalog
Go to [dashboard.rastro.ai/photo-studio](https://dashboard.rastro.ai/photo-studio), or click **Studio** (the camera icon) in the dashboard sidebar. Then choose your catalog in the picker at the top of the left panel — the panel fills with your products.
You can also upload loose images or videos with the upload button, and search your catalog with the search icon.
## 2. Select a product and describe your edit
Click a product card to load its image onto the canvas, then describe the change you want in the prompt box. A few examples:
* `Place this chair in a bright Scandinavian living room with natural window light`
* `Put the product on a pure white background for e-commerce`
* `Macro close-up of the fabric texture, soft studio lighting`
You can also **drag an image into the prompt box** to use it as a style or context reference.
Before you apply, the bottom toolbar lets you tune the run:
* **Fast / Slow** — Fast is cheaper and quicker; Slow uses the highest-quality model.
* **Images** — how many options to generate in one run (1–20). Generating several lets you pick the best one.
* **Templates** — reusable prompts for common shots (see below).
## 3. Apply and let it generate
Click **Apply**. The job runs in the background — the left panel switches to the **Results** tab, where each run shows its input → output thumbnails and a live status. You can keep working, queue edits on other products, or leave the page while it runs.
Generation typically takes under a minute for images. You are only charged credits for successful generations.
## 4. Review the result and save it to your catalog
When the run completes, the result opens on the canvas. Use the buttons in the top-right corner to **download** the image or **save it to your catalog**.
Saving **appends the image to the product's `image_urls` gallery** — your original photos are never overwritten. The product card updates immediately.
Back in the catalog view, the new image sits alongside the original on the item — ready to use anywhere your catalog is consumed (exports, the public API, connected channels).
## Generate videos too
Switch the tool from **Images** to **Video** to turn a product photo into a short clip. Pick a duration (**5s / 10s / 15s**), describe the motion, and optionally drag an image into the prompt box to use as the end frame.
Finished videos appear in the same Results tab and can be downloaded, trimmed, or saved to the product's `video_urls` just like images.
***
## What's Next
All tools, templates, costs, and behaviors
Create the catalog Photo Studio works on
How credits and billing work
# Photo Studio Reference
Source: https://docs.rastro.ai/photo-studio/reference
Tools, templates, results, and credit costs in Photo Studio
Photo Studio lives at [dashboard.rastro.ai/photo-studio](https://dashboard.rastro.ai/photo-studio) (**Studio** in the sidebar). It always operates on a selected **catalog**: products come from the catalog, generation history is kept per catalog, and results save back to catalog items. If you don't have a catalog yet, start with the [Catalogs Quickstart](/catalogs/quickstart).
## Tools
| Tool | What it does | Key controls |
| ---------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
| **Images** | Generates edited versions of the selected product image from your prompt | **Fast / Slow** quality toggle, **Images** count (1–20), reference image via drag & drop |
| **Video** | Turns the product image into a short clip | Duration **5s / 10s / 15s**, optional end-frame image via drag & drop |
### Quality: Fast vs Slow
The **Fast / Slow** switch picks the model tier for image generation:
* **Fast** — quickest and cheapest; great for iterating on prompt ideas.
* **Slow** (default) — the highest-quality model; use it for final shots.
### Multiple options per run
Set **Images** to generate up to 20 candidates from one prompt. Each generated image is billed separately, so 4 images cost 4× the single-image price. Generating a handful and picking the best result usually beats re-prompting one at a time.
### Reference images
Drag any image into the prompt box to guide the generation:
* **Images tool** — the drop becomes a *style/context reference* alongside your prompt.
* **Video tool** — the drop becomes the *end frame*, and the clip animates from the product image toward it.
## Prompt templates
Click **Templates** in the toolbar to open the **Template Library** — ready-made prompts for common product shots (lifestyle context, e-commerce hero on white, detail close-ups, resolution enhancement, marketplace image sets, and more). Selecting a template fills the prompt box; you can edit it before applying.
To reuse a prompt you've written, type it in the prompt box, open **Templates**, give it a name under **Save Current Prompt**, and click **Save**. Templates are shared across your organization.
## Results and history
The left panel's **Results** tab is the generation history for the selected catalog. Each run shows its input → output thumbnails, the prompt, a relative timestamp, and a status dot (blue pulsing = processing, green = completed, red = failed). Hover a run to **pin** it (pins are shared with your team) or **delete** it.
Click any completed run to reopen its result on the canvas:
* **Download** the file
* **View full size**
* **Save to catalog** (see below)
* **Trim** (videos only) — cut the clip before downloading or saving
## Saving results to your catalog
Saving a result attaches it to the product it was generated from:
* Images are **appended to the item's `image_urls`** gallery; videos go to `video_urls`.
* Existing media is never overwritten or removed — saves are additive and deduplicated.
* The change is a normal catalog item update, so it's immediately visible in the catalog view, exports, and the [public items API](/catalogs/reference).
To make a generated image the product's hero image, reorder the gallery afterwards — `image_urls[0]` is the hero.
## Credits and costs
Photo Studio draws from your organization's shared credit balance — the same pool used by enrichment. The left panel always shows **`Credits: / `** for the operation you've configured, before you click **Apply**. You are only charged for **successful** generations.
Typical costs:
| Operation | Credits |
| ---------------------------- | ------------- |
| Image generation/edit — Fast | 1–2 per image |
| Image generation/edit — Slow | 2–4 per image |
| Video — 5s | 10 |
| Video — 10s | 20 |
| Video — 15s | 30 |
See [Credits](/credits) for how balances, allowances, and packs work.
## Working with catalogs
* **Catalog required** — every generation and save is scoped to the selected catalog. Use the catalog picker's **Select a catalog** entry to create one on the spot if needed.
* **Products vs variants** — for catalogs with [product variants](/catalogs/reference), the layers toggle in the left panel switches between product-level and variant-level views; saves target whichever level you're working on.
* **Upload** — the upload button in the left panel accepts loose images and videos and routes them into catalog items.
* **Search & pagination** — search across all item fields with the search icon; large catalogs paginate at the bottom of the panel.
## Availability
Photo Studio is included on paid plans. On free organizations the **Studio** tab is visible but locked.
# Quickstart
Source: https://docs.rastro.ai/quickstart
Enrich product data with source citations in under 2 minutes.
## 1. Get your API key
[dashboard.rastro.ai/settings/api-keys](https://dashboard.rastro.ai/settings/api-keys) → Create New Key → Copy it.
## 2. Enrich your first item
```python Python theme={null}
import requests
response = requests.post(
"https://catalogapi.rastro.ai/api/public/enrich",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"},
{"name": "manufacturer", "type": "string", "description": "Brand or manufacturer name"}
],
"speed": "fast"
}
)
print(response.json())
```
```bash cURL theme={null}
curl -X POST "https://catalogapi.rastro.ai/api/public/enrich" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [{"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"}],
"output_schema": [
{"name": "bore_diameter", "type": "string", "description": "Inner diameter in mm"},
{"name": "outer_diameter", "type": "string", "description": "Outer diameter in mm"},
{"name": "manufacturer", "type": "string", "description": "Brand or manufacturer name"}
],
"speed": "fast"
}'
```
```typescript TypeScript theme={null}
const response = await fetch("https://catalogapi.rastro.ai/api/public/enrich", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
items: [{ part_number: "6205-2RS", name: "Deep Groove Ball Bearing" }],
output_schema: [
{ name: "bore_diameter", type: "string", description: "Inner diameter in mm" },
{ name: "outer_diameter", type: "string", description: "Outer diameter in mm" },
{ name: "manufacturer", type: "string", description: "Brand or manufacturer name" }
],
speed: "fast"
})
});
console.log(await response.json());
```
Use `"speed": "fast"` for quick testing. For production, omit `speed` or use `"deep"` for thorough results; `"slow"` is accepted as an alias.
## 3. Get your results
```json theme={null}
{
"job_id": "abc123-...",
"results": [{
"original_data": {"part_number": "6205-2RS", "name": "Deep Groove Ball Bearing"},
"after_data": {
"part_number": "6205-2RS",
"name": "Deep Groove Ball Bearing",
"bore_diameter": "25 mm",
"outer_diameter": "52 mm",
"manufacturer": "SKF",
"sources": {
"bore_diameter": ["https://skf.com/products/bearings/6205-2RS"],
"outer_diameter": ["https://skf.com/products/bearings/6205-2RS"],
"manufacturer": ["https://skf.com/products/bearings/6205-2RS"]
}
},
"all_sources": ["https://skf.com/products/bearings/6205-2RS"]
}],
"total_items": 1,
"successful": 1,
"credits_used": 1,
"status": "completed"
}
```
Each result includes `all_sources` — the URLs used to find the data.
***
All parameters and response fields
Async mode, domain filtering, taxonomy, quality scoring
Run this through Codex/Claude with activity-first review flow