# 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. Photo Studio start screen with a catalog selected and its products listed in the left panel 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. Photo Studio with a product selected on the canvas and an edit prompt typed into the prompt box 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**. Completed Photo Studio result showing the product composited into a new scene, with download and save buttons Saving **appends the image to the product's `image_urls` gallery** — your original photos are never overwritten. The product card updates immediately. Photo Studio confirming the image was saved to the catalog with a toast notification 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). Catalog item detail view showing the original product photo and the new generated image in its gallery ## 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. Photo Studio in Video mode showing duration options of 5, 10, and 15 seconds 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. Photo Studio Template Library popover showing built-in prompt templates and the Save Current Prompt field ## 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 Photo Studio results view showing a finished product video with download and trim controls and the run history on the left ## 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