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
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.
/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 |
|---|---|---|
file | Yes | The file to import (CSV, Excel, JSON, XML) |
instruction | Yes | Plain-English instruction, e.g. "Import this file into my contacts table" |
callback_url | No | Webhook URL for async processing |
callback_metadata | No | JSON string — echoed back in the webhook payload |
thread_id | No | Conversation 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"
}
/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" }
}
/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
}
/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.
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
}
/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"
}
/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 }
]
}
/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 }
]
}
/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.
/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
}
/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 |
|---|---|---|
file | Yes | CSV or Excel file |
key_columns | Yes | Comma-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>
/api/v1/import-history
List past imports with optional filters.
| Query param | Description |
|---|---|
limit | Results per page (default: 10) |
offset | Pagination offset |
table_name | Filter by destination table |
status | Filter by import status |
Use GET /api/v1/import-history/{import_id} to retrieve full details for a single import by its UUID.
/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_metadata | Set 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.
Rows skipped because they matched an existing record
Rows that failed a validation rule (e.g. invalid email format)
Field values that were automatically replaced during soft validation
/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.
/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 |
|---|---|
| Duplicates | insert, skip |
| Validation failures | insert_as_is, insert_corrected, discarded |
| Value replacements | restore_original, keep_replacement, set_custom |
/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.
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 |
|---|---|
400 | Bad request — missing or invalid parameter |
401 | Unauthorized — missing or invalid API key |
403 | Forbidden — API key lacks permission for this operation |
404 | Not found — table, import, or record does not exist |
422 | Validation error — request body failed schema validation |
500 | Server 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" } }
]
}