Skip to main content
Arcanflows

Data Tables API

Complete API reference for managing data tables, records, imports, and field automation jobs.

Overview

The Data Tables API provides a structured data storage layer for your agents and workflows. Tables support typed fields, record versioning, file attachments, bulk operations, CSV/JSON imports, and automated field jobs powered by AI.


Data Table Endpoints

MethodEndpointDescription
GET/v1/data-tablesList tables
POST/v1/data-tablesCreate table
GET/v1/data-tables/:idGet table
GET/v1/data-tables/slug/:slugGet table by slug
PUT/v1/data-tables/:idUpdate table
DELETE/v1/data-tables/:idDelete table
POST/v1/data-tables/:id/duplicateDuplicate table
POST/v1/data-tables/:id/archiveArchive table
POST/v1/data-tables/:id/restoreRestore archived table

Record Endpoints

MethodEndpointDescription
GET/v1/data-tables/:id/recordsList records
POST/v1/data-tables/:id/recordsCreate record
GET/v1/data-tables/:id/records/:recordIdGet record
PUT/v1/data-tables/:id/records/:recordIdUpdate record
DELETE/v1/data-tables/:id/records/:recordIdDelete record
POST/v1/data-tables/:id/records/bulkBulk create records
DELETE/v1/data-tables/:id/records/bulkBulk delete records
GET/v1/data-tables/:id/records/:recordId/historyRecord history
POST/v1/data-tables/:id/records/:recordId/restoreRestore record version

Field Endpoints

MethodEndpointDescription
POST/v1/data-tables/:id/fieldsAdd field
PUT/v1/data-tables/:id/fields/:fieldIdUpdate field
DELETE/v1/data-tables/:id/fields/:fieldIdDelete field
POST/v1/data-tables/:id/fields/reorderReorder fields

File Endpoints

MethodEndpointDescription
POST/v1/data-tables/:id/filesUpload file
GET/v1/data-tables/:id/files/:fileIdDownload file
DELETE/v1/data-tables/:id/files/:fileIdDelete file

Import Endpoints

MethodEndpointDescription
GET/v1/data-tables/:id/importList import jobs
POST/v1/data-tables/:id/importCreate import job
POST/v1/data-tables/:id/import/previewPreview import
GET/v1/data-tables/:id/import/:jobIdGet job status
PUT/v1/data-tables/:id/import/:jobIdUpdate job mapping
POST/v1/data-tables/:id/import/:jobId/executeExecute import
POST/v1/data-tables/:id/import/:jobId/cancelCancel import
GET/v1/data-tables/:id/import/:jobId/progressGet progress
DELETE/v1/data-tables/:id/import/:jobIdDelete job

Field Job Endpoints

MethodEndpointDescription
GET/v1/data-tables/:tableId/jobsList field jobs
POST/v1/data-tables/:tableId/jobsCreate field job
GET/v1/data-tables/:tableId/jobs/:jobIdGet job
PUT/v1/data-tables/:tableId/jobs/:jobIdUpdate job
DELETE/v1/data-tables/:tableId/jobs/:jobIdDelete job
POST/v1/data-tables/:tableId/jobs/:jobId/executeExecute job
POST/v1/data-tables/:tableId/jobs/:jobId/enableToggle enable/disable
GET/v1/data-tables/:tableId/jobs/:jobId/executionsJob execution history
GET/v1/data-tables/:tableId/records/:recordId/executionsRecord job executions

List Tables

GET /v1/data-tables

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger20Items per page (max 100)
searchstring-Search by name or description
statusstring-Filter: active, archived
sortstring-created_atSort field (- prefix for descending)

Response

json
{
  "data": [
    {
      "id": "dt_abc123",
      "name": "Customer Leads",
      "slug": "customer-leads",
      "description": "Inbound leads from marketing campaigns",
      "status": "active",
      "field_count": 8,
      "record_count": 1450,
      "created_at": "2025-11-15T09:00:00Z",
      "updated_at": "2025-12-10T14:20:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 5,
    "total_pages": 1
  }
}

Example

bash
curl "https://api.arcanflows.io/v1/data-tables?status=active" \
  -H "Authorization: Bearer your_api_key"

Create Table

POST /v1/data-tables

Request Body

json
{
  "name": "Customer Leads",
  "description": "Inbound leads from marketing campaigns",
  "fields": [
    {
      "name": "Full Name",
      "slug": "full_name",
      "type": "text",
      "required": true,
      "description": "Contact full name"
    },
    {
      "name": "Email",
      "slug": "email",
      "type": "email",
      "required": true,
      "unique": true
    },
    {
      "name": "Phone",
      "slug": "phone",
      "type": "phone",
      "required": false
    },
    {
      "name": "Lead Score",
      "slug": "lead_score",
      "type": "number",
      "config": {
        "min": 0,
        "max": 100,
        "decimal_places": 0
      }
    },
    {
      "name": "Status",
      "slug": "status",
      "type": "select",
      "config": {
        "options": [
          { "value": "new", "label": "New", "color": "#73E0E7" },
          { "value": "contacted", "label": "Contacted", "color": "#F59E0B" },
          { "value": "qualified", "label": "Qualified", "color": "#10B981" },
          { "value": "lost", "label": "Lost", "color": "#EF4444" }
        ]
      }
    },
    {
      "name": "Tags",
      "slug": "tags",
      "type": "multi_select",
      "config": {
        "options": [
          { "value": "enterprise", "label": "Enterprise" },
          { "value": "smb", "label": "SMB" },
          { "value": "inbound", "label": "Inbound" }
        ]
      }
    },
    {
      "name": "Website",
      "slug": "website",
      "type": "url"
    },
    {
      "name": "Created Date",
      "slug": "created_date",
      "type": "date"
    }
  ]
}

Field Types

TypeDescriptionConfig Options
textSingle-line textmax_length
rich_textRich text / markdownmax_length
numberNumeric valuemin, max, decimal_places
emailEmail addressAuto-validated
urlURLAuto-validated
phonePhone numberformat
dateDate onlymin_date, max_date
datetimeDate and timemin_date, max_date, timezone
booleanTrue/false toggledefault_value
selectSingle select dropdownoptions (array of value/label/color)
multi_selectMultiple selectoptions, max_selections
fileFile attachmentallowed_types, max_size_mb
referenceLink to another tabletable_id, display_field
formulaComputed fieldexpression

Response

json
{
  "data": {
    "id": "dt_abc123",
    "name": "Customer Leads",
    "slug": "customer-leads",
    "description": "Inbound leads from marketing campaigns",
    "status": "active",
    "fields": [
      {
        "id": "fld_001",
        "name": "Full Name",
        "slug": "full_name",
        "type": "text",
        "required": true,
        "order": 0
      }
    ],
    "field_count": 8,
    "record_count": 0,
    "created_at": "2025-12-10T10:00:00Z",
    "updated_at": "2025-12-10T10:00:00Z"
  }
}

Get Table

GET /v1/data-tables/:id

Returns the full table definition including all fields and metadata.

Response

json
{
  "data": {
    "id": "dt_abc123",
    "name": "Customer Leads",
    "slug": "customer-leads",
    "description": "Inbound leads from marketing campaigns",
    "status": "active",
    "fields": [
      {
        "id": "fld_001",
        "name": "Full Name",
        "slug": "full_name",
        "type": "text",
        "required": true,
        "order": 0
      },
      {
        "id": "fld_002",
        "name": "Email",
        "slug": "email",
        "type": "email",
        "required": true,
        "order": 1
      }
    ],
    "field_count": 8,
    "record_count": 1450,
    "created_at": "2025-11-15T09:00:00Z",
    "updated_at": "2025-12-10T14:20:00Z"
  }
}

Get Table by Slug

GET /v1/data-tables/slug/:slug

Retrieve a table using its URL-friendly slug instead of its UUID.

bash
curl "https://api.arcanflows.io/v1/data-tables/slug/customer-leads" \
  -H "Authorization: Bearer your_api_key"

Update Table

PUT /v1/data-tables/:id

Request Body

json
{
  "name": "Updated Lead Tracker",
  "description": "Updated description"
}

Returns the updated table object.


Delete Table

DELETE /v1/data-tables/:id

Response

HTTP/1.1 204 No Content

Warning: This permanently deletes the table and all its records. Use archive for data retention.


Duplicate Table

POST /v1/data-tables/:id/duplicate

Creates a copy of the table structure. Optionally copies records.

Request Body

json
{
  "name": "Customer Leads (Copy)",
  "include_records": false
}

Archive / Restore Table

POST /v1/data-tables/:id/archive
POST /v1/data-tables/:id/restore

Archive hides the table from default listings and prevents new records. Restore reverses this.


Field Management

Add Field

POST /v1/data-tables/:id/fields
json
{
  "name": "Company Size",
  "slug": "company_size",
  "type": "select",
  "required": false,
  "config": {
    "options": [
      { "value": "1-10", "label": "1-10 employees" },
      { "value": "11-50", "label": "11-50 employees" },
      { "value": "51-200", "label": "51-200 employees" },
      { "value": "200+", "label": "200+ employees" }
    ]
  }
}

Update Field

PUT /v1/data-tables/:id/fields/:fieldId
json
{
  "name": "Organization Size",
  "required": true
}

Delete Field

DELETE /v1/data-tables/:id/fields/:fieldId

Warning: Deleting a field removes that field's data from all existing records.

Reorder Fields

POST /v1/data-tables/:id/fields/reorder
json
{
  "field_order": ["fld_002", "fld_001", "fld_003", "fld_004"]
}

File Operations

Upload File

POST /v1/data-tables/:id/files

Upload a file attachment. Use multipart/form-data.

bash
curl -X POST "https://api.arcanflows.io/v1/data-tables/dt_abc123/files" \
  -H "Authorization: Bearer your_api_key" \
  -F "[email protected]" \
  -F "record_id=rec_xyz789" \
  -F "field_id=fld_005"

Response

json
{
  "data": {
    "id": "file_001",
    "filename": "document.pdf",
    "mime_type": "application/pdf",
    "size_bytes": 245000,
    "record_id": "rec_xyz789",
    "field_id": "fld_005",
    "created_at": "2025-12-10T10:00:00Z"
  }
}

Download File

GET /v1/data-tables/:id/files/:fileId

Returns the file binary with the appropriate Content-Type header.

Delete File

DELETE /v1/data-tables/:id/files/:fileId

List Records

GET /v1/data-tables/:id/records

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger50Items per page (max 200)
sortstring-created_atSort by field slug (- for descending)
searchstring-Full-text search across all text fields
filterstring-JSON-encoded filter object

Filter Syntax

Filters are passed as a JSON-encoded query parameter. Operators: eq, neq, gt, gte, lt, lte, contains, not_contains, starts_with, ends_with, is_empty, is_not_empty, in, not_in.

bash
# Filter by status = "qualified" and lead_score >= 70
curl "https://api.arcanflows.io/v1/data-tables/dt_abc123/records?filter=%7B%22and%22%3A%5B%7B%22field%22%3A%22status%22%2C%22op%22%3A%22eq%22%2C%22value%22%3A%22qualified%22%7D%2C%7B%22field%22%3A%22lead_score%22%2C%22op%22%3A%22gte%22%2C%22value%22%3A70%7D%5D%7D" \
  -H "Authorization: Bearer your_api_key"

The decoded filter:

json
{
  "and": [
    { "field": "status", "op": "eq", "value": "qualified" },
    { "field": "lead_score", "op": "gte", "value": 70 }
  ]
}

You can also use or for disjunctive filters:

json
{
  "or": [
    { "field": "status", "op": "eq", "value": "new" },
    { "field": "status", "op": "eq", "value": "contacted" }
  ]
}

Response

json
{
  "data": [
    {
      "id": "rec_xyz789",
      "values": {
        "full_name": "Jane Smith",
        "email": "[email protected]",
        "phone": "+1-555-987-6543",
        "lead_score": 85,
        "status": "qualified",
        "tags": ["enterprise", "inbound"],
        "website": "https://acme.com",
        "created_date": "2025-12-01"
      },
      "created_at": "2025-12-01T08:30:00Z",
      "updated_at": "2025-12-09T16:45:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 50,
    "total": 1450,
    "total_pages": 29
  }
}

Create Record

POST /v1/data-tables/:id/records

Request Body

json
{
  "values": {
    "full_name": "John Doe",
    "email": "[email protected]",
    "phone": "+1-555-123-4567",
    "lead_score": 60,
    "status": "new",
    "tags": ["smb", "inbound"],
    "website": "https://example.com",
    "created_date": "2025-12-10"
  }
}

Response

json
{
  "data": {
    "id": "rec_new001",
    "values": {
      "full_name": "John Doe",
      "email": "[email protected]",
      "phone": "+1-555-123-4567",
      "lead_score": 60,
      "status": "new",
      "tags": ["smb", "inbound"],
      "website": "https://example.com",
      "created_date": "2025-12-10"
    },
    "created_at": "2025-12-10T10:00:00Z",
    "updated_at": "2025-12-10T10:00:00Z"
  }
}

Get Record

GET /v1/data-tables/:id/records/:recordId

Returns a single record with all field values.


Update Record

PUT /v1/data-tables/:id/records/:recordId

Only include the fields you want to update:

json
{
  "values": {
    "lead_score": 85,
    "status": "qualified"
  }
}

Delete Record

DELETE /v1/data-tables/:id/records/:recordId

Response

HTTP/1.1 204 No Content

Bulk Create Records

POST /v1/data-tables/:id/records/bulk

Create up to 1000 records in a single request.

json
{
  "records": [
    {
      "values": {
        "full_name": "Alice Johnson",
        "email": "[email protected]",
        "status": "new"
      }
    },
    {
      "values": {
        "full_name": "Bob Williams",
        "email": "[email protected]",
        "status": "new"
      }
    }
  ]
}

Response

json
{
  "data": {
    "created": 2,
    "failed": 0,
    "errors": []
  }
}

Bulk Delete Records

DELETE /v1/data-tables/:id/records/bulk
json
{
  "record_ids": ["rec_001", "rec_002", "rec_003"]
}

Response

json
{
  "data": {
    "deleted": 3
  }
}

Record History

GET /v1/data-tables/:id/records/:recordId/history

Returns a version history of all changes to the record.

Response

json
{
  "data": [
    {
      "version": 3,
      "changed_fields": ["lead_score", "status"],
      "previous_values": {
        "lead_score": 60,
        "status": "contacted"
      },
      "new_values": {
        "lead_score": 85,
        "status": "qualified"
      },
      "changed_by": "user_abc",
      "changed_at": "2025-12-09T16:45:00Z"
    },
    {
      "version": 2,
      "changed_fields": ["status"],
      "previous_values": { "status": "new" },
      "new_values": { "status": "contacted" },
      "changed_by": "user_abc",
      "changed_at": "2025-12-05T11:30:00Z"
    }
  ]
}

Restore Record Version

POST /v1/data-tables/:id/records/:recordId/restore
json
{
  "version": 2
}

Restores the record to a previous version from its history.


Data Import

Create Import Job

POST /v1/data-tables/:id/import

Upload a CSV or JSON file to create an import job. Use multipart/form-data.

bash
curl -X POST "https://api.arcanflows.io/v1/data-tables/dt_abc123/import" \
  -H "Authorization: Bearer your_api_key" \
  -F "[email protected]" \
  -F "format=csv" \
  -F "has_header=true"

Response

json
{
  "data": {
    "id": "imp_001",
    "status": "pending",
    "format": "csv",
    "total_rows": 500,
    "column_mapping": {
      "Name": null,
      "Email Address": null,
      "Score": null
    },
    "created_at": "2025-12-10T10:00:00Z"
  }
}

Preview Import

POST /v1/data-tables/:id/import/preview

Returns a sample of rows and detected columns without creating a job.

bash
curl -X POST "https://api.arcanflows.io/v1/data-tables/dt_abc123/import/preview" \
  -H "Authorization: Bearer your_api_key" \
  -F "[email protected]" \
  -F "format=csv"

Response

json
{
  "data": {
    "columns": ["Name", "Email Address", "Score", "Status"],
    "sample_rows": [
      ["Jane Smith", "[email protected]", "85", "qualified"],
      ["John Doe", "[email protected]", "60", "new"]
    ],
    "total_rows": 500,
    "suggested_mapping": {
      "Name": "full_name",
      "Email Address": "email",
      "Score": "lead_score",
      "Status": "status"
    }
  }
}

Update Job Mapping

PUT /v1/data-tables/:id/import/:jobId

Set the column-to-field mapping before executing:

json
{
  "column_mapping": {
    "Name": "full_name",
    "Email Address": "email",
    "Score": "lead_score"
  },
  "on_duplicate": "skip"
}
on_duplicateBehavior
skipSkip rows that match an existing record
updateUpdate existing records with new values
createAlways create new records (allow duplicates)

Execute Import

POST /v1/data-tables/:id/import/:jobId/execute

Starts the import. Large imports run asynchronously.

Response

json
{
  "data": {
    "id": "imp_001",
    "status": "processing",
    "total_rows": 500,
    "processed_rows": 0
  }
}

Get Import Progress

GET /v1/data-tables/:id/import/:jobId/progress
json
{
  "data": {
    "id": "imp_001",
    "status": "processing",
    "total_rows": 500,
    "processed_rows": 320,
    "created": 310,
    "updated": 0,
    "skipped": 8,
    "failed": 2,
    "errors": [
      { "row": 145, "error": "Invalid email format" },
      { "row": 289, "error": "Required field 'full_name' is empty" }
    ],
    "percent_complete": 64
  }
}

Cancel Import

POST /v1/data-tables/:id/import/:jobId/cancel

Cancels a running import. Records already imported are retained.

List Import Jobs

GET /v1/data-tables/:id/import

Returns all import jobs for the table with their statuses.

Delete Import Job

DELETE /v1/data-tables/:id/import/:jobId

Removes a completed or cancelled import job record (does not delete imported data).


Field Jobs (Automation)

Field jobs allow you to automate data enrichment, transformation, and AI-powered field population across your table records.

Create Field Job

POST /v1/data-tables/:tableId/jobs
json
{
  "name": "Enrich Lead Score",
  "description": "Use AI to score leads based on company and title",
  "target_field_id": "fld_004",
  "source_field_ids": ["fld_001", "fld_002"],
  "job_type": "ai_enrichment",
  "config": {
    "model": "gpt-4o",
    "prompt": "Based on the contact name and email domain, estimate a lead quality score from 0-100.",
    "output_type": "number"
  },
  "trigger": "on_create",
  "is_enabled": true
}

Job Configuration

FieldTypeRequiredDescription
namestringYesJob name
target_field_idstringYesField to populate
source_field_idsarrayYesFields used as input
job_typestringYesai_enrichment, formula, lookup, transform
configobjectYesType-specific configuration
triggerstringYeson_create, on_update, manual, scheduled
is_enabledbooleanNoDefault true

Execute Field Job

POST /v1/data-tables/:tableId/jobs/:jobId/execute

Run the job manually against all records (or a filtered subset):

json
{
  "filter": {
    "field": "lead_score",
    "op": "is_empty"
  }
}

Response

json
{
  "data": {
    "execution_id": "exec_001",
    "status": "processing",
    "total_records": 230,
    "processed": 0
  }
}

Toggle Enable/Disable

POST /v1/data-tables/:tableId/jobs/:jobId/enable
json
{
  "is_enabled": false
}

Job Execution History

GET /v1/data-tables/:tableId/jobs/:jobId/executions
json
{
  "data": [
    {
      "id": "exec_001",
      "status": "completed",
      "total_records": 230,
      "processed": 230,
      "succeeded": 225,
      "failed": 5,
      "started_at": "2025-12-10T10:00:00Z",
      "completed_at": "2025-12-10T10:02:30Z"
    }
  ]
}

Record Executions

GET /v1/data-tables/:tableId/records/:recordId/executions

Shows all field job executions that have run against a specific record.


Code Examples

Python

python
import requests

API_URL = "https://api.arcanflows.io/v1"
HEADERS = {"Authorization": "Bearer your_api_key"}

# Create a table
table = requests.post(f"{API_URL}/data-tables", headers=HEADERS, json={
    "name": "Contacts",
    "fields": [
        {"name": "Name", "slug": "name", "type": "text", "required": True},
        {"name": "Email", "slug": "email", "type": "email", "required": True},
        {"name": "Score", "slug": "score", "type": "number"}
    ]
}).json()["data"]

# Add records
requests.post(
    f"{API_URL}/data-tables/{table['id']}/records",
    headers=HEADERS,
    json={"values": {"name": "Alice", "email": "[email protected]", "score": 90}}
)

# List records with filter
records = requests.get(
    f"{API_URL}/data-tables/{table['id']}/records",
    headers=HEADERS,
    params={"filter": '{"field":"score","op":"gte","value":80}'}
).json()["data"]

for rec in records:
    print(f"{rec['values']['name']}: {rec['values']['score']}")

JavaScript

javascript
const API_URL = 'https://api.arcanflows.io/v1';
const headers = {
  'Authorization': 'Bearer your_api_key',
  'Content-Type': 'application/json',
};

// Create table
const table = await fetch(`${API_URL}/data-tables`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    name: 'Contacts',
    fields: [
      { name: 'Name', slug: 'name', type: 'text', required: true },
      { name: 'Email', slug: 'email', type: 'email', required: true },
    ],
  }),
}).then(r => r.json());

// Bulk create records
await fetch(`${API_URL}/data-tables/${table.data.id}/records/bulk`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    records: [
      { values: { name: 'Alice', email: '[email protected]' } },
      { values: { name: 'Bob', email: '[email protected]' } },
    ],
  }),
});

// Import from CSV
const formData = new FormData();
formData.append('file', csvFile);
formData.append('format', 'csv');
formData.append('has_header', 'true');

const importJob = await fetch(
  `${API_URL}/data-tables/${table.data.id}/import`,
  { method: 'POST', headers: { Authorization: headers.Authorization }, body: formData }
).then(r => r.json());