Public API Guide

Everything you need to import data, query it in plain English, and manage it programmatically through the REST API.

Fully Hosted Interactive reference: /docs (Swagger)  ·  /redoc (ReDoc)

Obtain an API Key

Your API key is issued by the Content Atlas team when your account is provisioned. It looks like:

atlas_live_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Store it securely. Your API key is shown only once at creation time.

Authentication

Include your key in every request via the X-API-Key header:

X-API-Key: atlas_live_sk_...

Your API key is bound to your organization — you never need to pass an organization ID in requests; it is resolved automatically.

Base URL

https://your-instance.contentatlas.io

All endpoints are relative to this base URL. Replace it with the hostname provided by the Content Atlas team. All endpoints below are prefixed with /api/v1.

How It Works

Organization isolation

All your data lives in a dedicated, isolated namespace. Your API key resolves your organization automatically — no org ID needed in requests.

Your LLM, our infrastructure

Natural language queries and smart imports are powered by the LLM provider you configured with the Content Atlas team. No additional API-side setup required.

Logical table names

When you import a file into a table called customers, you use that same name across all subsequent API calls. Physical namespacing is handled transparently.

Sync vs. async imports

Import calls are synchronous by default. For large files, pass a callback_url to switch to fire-and-forget mode — the API returns immediately with status: "pending" and POSTs the result to your URL when done.

POST /api/v1/smart-import

The primary import endpoint. Upload any file (CSV, Excel, JSON, XML) with a plain-English instruction. The AI handles schema detection, column mapping, and duplicate checking automatically.

Request multipart/form-data

Field Required Description
fileYesThe file to import (CSV, Excel, JSON, XML)
instructionYesPlain-English instruction, e.g. "Import this file into my contacts table"
callback_urlNoWebhook URL for async processing
callback_metadataNoJSON string — echoed back in the webhook payload
thread_idNoConversation ID for multi-turn context
POST /api/v1/smart-import
X-API-Key: atlas_live_sk_...
Content-Type: multipart/form-data

instruction=Import this into my leads table and flag duplicates
file=<contacts.csv>

Response — sync

{
  "status": "success",
  "import_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "table_name": "leads",
  "file_name": "contacts.csv",
  "records_processed": 1000,
  "duplicates_skipped": 5,
  "action_taken": "import",
  "response_summary": "Imported 995 leads. 5 duplicates were skipped.",
  "has_correctable_issues": true
}

Response — async when callback_url is set

{
  "status": "pending",
  "message": "Import of 'contacts.csv' is being processed asynchronously.",
  "file_name": "contacts.csv"
}
POST /api/v1/smart-import-b2

Same as smart-import but the source file is already in object storage (B2/S3). Provide the storage path instead of uploading a file.

Request application/json

{
  "file_name": "exports/contacts_2024.csv",
  "instruction": "Import into my contacts table",
  "target_table_name": "contacts",
  "callback_url": "https://your-app.com/webhooks/import",
  "callback_metadata": { "job_id": "abc123" }
}
POST /api/v1/smart-data-instruction

Insert, update, or archive individual records using natural language or structured JSON. No file upload needed — ideal for single-record operations triggered by other systems.

Supported intents: insert, update, archive.

Request — Markdown / plain text

POST /api/v1/smart-data-instruction
X-API-Key: atlas_live_sk_...
Content-Type: text/markdown

Add a new client:

## Client
- Name: Acme Corp
- Email: billing@acme.com
- Phone: +1-800-555-0100

Request — JSON

{
  "intent": "insert",
  "entities": [
    {
      "entity_type": "client",
      "table_hint": "clients",
      "data": {
        "name": "Acme Corp",
        "email": "billing@acme.com",
        "phone": "+1-800-555-0100"
      }
    }
  ]
}

Response

{
  "success": true,
  "intent": "insert",
  "entities": [
    {
      "entity_type": "client",
      "table_name": "clients",
      "success": true,
      "operation": "INSERT",
      "record_count": 1,
      "inserted_ids": ["9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"]
    }
  ],
  "tables_affected": ["clients"],
  "total_records_processed": 1
}
POST /api/v1/query

Ask a question about your data in plain English. Returns a conversational answer, the raw result set as CSV, and the SQL that was executed. Queries are read-only.

Use thread_id to maintain conversation context across multiple questions — the AI remembers previous turns in the thread.

Request

{
  "prompt": "Show me all leads from California added in the last 30 days",
  "table_name": "leads",
  "max_rows": 500,
  "thread_id": "optional-conversation-id"
}

Response

{
  "success": true,
  "response": "Found 87 leads from California added in the last 30 days.",
  "executed_sql": "SELECT * FROM leads WHERE state = 'CA' AND created_at >= NOW() - INTERVAL '30 days'",
  "data_csv": "name,email,state,created_at\nJohn Doe,john@example.com,CA,2024-05-15\n...",
  "rows_returned": 87,
  "execution_time_seconds": 0.42
}
POST /api/v1/generate-sql

Converts a natural language prompt to SQL without executing it. Useful as a probe step before triggering a large export — get the SQL first, review it, then execute via your own tooling.

Request

{
  "prompt": "Get all active customers with their email and company name",
  "table_hints": ["customers"]
}

Response

{
  "success": true,
  "sql_query": "SELECT email, company_name FROM \"customers\" WHERE status = 'active'",
  "tables_referenced": ["customers"],
  "explanation": "Selects email and company from the customers table filtered by active status"
}
GET /api/v1/tables

List all tables in your organization with their current row counts.

{
  "success": true,
  "tables": [
    { "table_name": "leads", "row_count": 12500 },
    { "table_name": "customers", "row_count": 4320 }
  ]
}
GET /api/v1/tables/{table_name}/schema

Get column definitions for a table.

{
  "success": true,
  "table_name": "leads",
  "columns": [
    { "name": "email",      "type": "VARCHAR",                   "nullable": false },
    { "name": "company",    "type": "VARCHAR",                   "nullable": true  },
    { "name": "created_at", "type": "TIMESTAMP WITH TIME ZONE",  "nullable": true  }
  ]
}
GET /api/v1/tables/{table_name}/stats

Get row count, column count, and column-level data types for a table. Useful for validation before constructing queries.

POST /api/v1/tables/{table_name}/update

Update rows in a table. Supports two modes:

Condition mode — update all rows matching a WHERE clause

{
  "mode": "condition",
  "condition": "campaign = 'spring_2024'",
  "updates": {
    "status": "archived"
  }
}

Batch mode — update specific rows by unique key

{
  "mode": "batch",
  "batch_updates": [
    {
      "keys":   { "email": "john@example.com" },
      "values": { "status": "active", "plan": "pro" }
    }
  ]
}

Response

{
  "success": true,
  "table_name": "customers",
  "rows_updated": 156
}
POST /api/v1/tables/{table_name}/update/file

Batch-update rows by uploading a CSV or Excel file. Each row must contain the key column(s) used to identify the record, plus any columns to update.

Request multipart/form-data

Field Required Description
fileYesCSV or Excel file
key_columnsYesComma-separated column names used to identify rows (e.g. email,account_id)
POST /api/v1/tables/customers/update/file
X-API-Key: atlas_live_sk_...
Content-Type: multipart/form-data

key_columns=email
file=<updates.csv>
GET /api/v1/import-history

List past imports with optional filters.

Query param Description
limitResults per page (default: 10)
offsetPagination offset
table_nameFilter by destination table
statusFilter by import status

Use GET /api/v1/import-history/{import_id} to retrieve full details for a single import by its UUID.

GET /api/v1/import/{import_id}/export

Stream all rows from a specific import as a CSV file. System columns are excluded by default.

Query param Description
include_metadataSet to true to include internal system columns

Response headers include X-Rows-Count and X-Table-Name.

Import Issues

When an import completes with has_correctable_issues: true, use these endpoints to review and resolve problems.

duplicates

Rows skipped because they matched an existing record

validations

Rows that failed a validation rule (e.g. invalid email format)

replacements

Field values that were automatically replaced during soft validation

GET /api/v1/import/{import_id}/issues

Summary of all issue types plus the first batch of each.

{
  "success": true,
  "import_id": "3fa85f64-...",
  "table_name": "leads",
  "summary": {
    "duplicates_count": 5,
    "validation_failures_count": 3,
    "value_replacements_count": 12,
    "has_issues": true
  },
  "duplicates": [...],
  "validation_failures": [...],
  "value_replacements": [...]
}

Use GET /api/v1/import/{import_id}/issues/{issue_type} to paginate a single issue type. issue_type is one of duplicates, validations, or replacements.

POST /api/v1/import/{import_id}/issues/correct

Submit resolutions for any combination of issue types in a single call.

{
  "duplicates": [
    { "record_number": 45, "action": "insert", "data": { "email": "new@example.com" } },
    { "record_number": 67, "action": "skip" }
  ],
  "validation_failures": [
    { "failure_id": 12, "action": "insert_corrected", "corrected_data": { "email": "fixed@example.com" } },
    { "failure_id": 13, "action": "discarded" }
  ],
  "value_replacements": [
    { "replacement_id": 88, "action": "restore_original" },
    { "replacement_id": 89, "action": "set_custom", "custom_value": "N/A" }
  ]
}

Available actions per type

Issue type Available actions
Duplicatesinsert, skip
Validation failuresinsert_as_is, insert_corrected, discarded
Value replacementsrestore_original, keep_replacement, set_custom
POST /api/v1/import/{import_id}/redo

Clears all mapping data and resets import state, optionally with a new mapping configuration. Set auto_execute: true to immediately re-run the import after resetting.

{
  "mapping": {
    "table_name": "leads",
    "mappings": { "email": "Email Address", "name": "Full Name" },
    "check_duplicates": true
  },
  "auto_execute": true
}

Use POST /api/v1/import/{import_id}/clear-mapping to remove only the mapping configuration without re-executing.

Async Imports & Webhooks

For large imports, pass callback_url to the import endpoint. The API returns immediately and POSTs the result to your URL when processing finishes.

Your webhook endpoint must return a 2xx status. Retries are not currently performed on failure — implement idempotent handling on your side.

Success payload

{
  "status": "success",
  "import_id": "3fa85f64-...",
  "file_name": "contacts.csv",
  "table_name": "leads",
  "records_imported": 9950,
  "duplicates_removed": 50,
  "timestamp": "2024-05-31T14:23:00Z",
  "metadata": { "job_id": "abc123" }
}

Failure payload

{
  "status": "failed",
  "file_name": "contacts.csv",
  "error": "File could not be parsed — unexpected encoding",
  "timestamp": "2024-05-31T14:23:05Z",
  "metadata": { "job_id": "abc123" }
}

The callback_metadata object you pass in the request is echoed back at the top level of the metadata field, so you can correlate responses with your own job IDs.

Error Reference

All errors follow the same shape:

{ "detail": "Human-readable description of what went wrong" }
HTTP Code Meaning
400Bad request — missing or invalid parameter
401Unauthorized — missing or invalid API key
403Forbidden — API key lacks permission for this operation
404Not found — table, import, or record does not exist
422Validation error — request body failed schema validation
500Server error — contact the Content Atlas team

Common Workflows

1. Import a CSV and query the results

# Step 1 — Import
POST /api/v1/smart-import
X-API-Key: atlas_live_sk_...
Content-Type: multipart/form-data

instruction=Import this into my leads table
file=<leads_q2.csv>

# → { "import_id": "abc-123", "status": "success", "records_processed": 2000 }

# Step 2 — Query
POST /api/v1/query
X-API-Key: atlas_live_sk_...
Content-Type: application/json

{ "prompt": "How many leads came from the US vs Canada?", "table_name": "leads" }

# → { "response": "1,450 from US, 550 from Canada.", "data_csv": "...", ... }

2. Bulk-update rows from a spreadsheet

Prepare a CSV with the identifying column(s) and the columns to update:

email,status,plan
alice@example.com,active,pro
bob@example.com,churned,free
POST /api/v1/tables/customers/update/file
X-API-Key: atlas_live_sk_...
Content-Type: multipart/form-data

key_columns=email
file=<customer_updates.csv>

# → { "success": true, "rows_updated": 2 }

3. Review and resolve data quality issues

# Step 1 — Check for issues
GET /api/v1/import/abc-123/issues
X-API-Key: atlas_live_sk_...

# → { "summary": { "duplicates_count": 3, "validation_failures_count": 1 }, ... }

# Step 2 — Fetch duplicate details
GET /api/v1/import/abc-123/issues/duplicates
X-API-Key: atlas_live_sk_...

# Step 3 — Submit corrections
POST /api/v1/import/abc-123/issues/correct
X-API-Key: atlas_live_sk_...
Content-Type: application/json

{
  "duplicates": [
    { "record_number": 45, "action": "insert" },
    { "record_number": 67, "action": "skip" }
  ],
  "validation_failures": [
    { "failure_id": 1, "action": "insert_corrected", "corrected_data": { "email": "fixed@example.com" } }
  ]
}