Skip to content

Workflow API

Integrate Kuration’s project workflow using the enterprise REST API (API v1.0 in the product UI). The same surface is documented in the app under /settings/integration?tab=api.

Base URL

https://api.kurationai.com/api/enterprise

All paths below are relative to this base (e.g. GET /projectshttps://api.kurationai.com/api/enterprise/projects).

Authentication

Send your API key on every request:

kur-api-key: YOUR_API_KEY

Use common JSON headers as needed:

Accept: application/json
Content-Type: application/json

Generate or rotate the key in Settings → Integrations → API (/settings/integration?tab=api).

Try it from this page

Each endpoint below includes a Try this request panel. Paste kur-api-key once — it is synced across panels and saved in sessionStorage for this tab only (never sent to the docs server). Requests go only to api.kurationai.com.

POST requests change live data

POST /projects creates a real project and may start builder work (quota/cost). POST .../rows inserts real rows and may trigger tools. Prefer low max_results and test workspaces while experimenting.

If a panel fails (CORS)

In-browser requests only work when this site’s origin is allowed by the API (local mkdocs serve, *.github.io, docs.kurationai.com, etc.). If you see a network error, use cURL or run JavaScript from your own app instead.

Plan gating

If your workspace does not have API access, endpoints that require a key will not be usable until your plan includes API access.

How it works

A typical automation flow:

  1. Submit company dataPOST .../projects/{project_id}/rows with a JSON body keyed by column names (see Save company data).
  2. Receive a row ID — The response includes row_id; Kuration may run tool columns asynchronously for that row.
  3. Fetch results — Poll GET .../projects/{project_id}/rows/{row_id} (or list rows with pagination) to read enriched column values.

You may also create projects from a builder with POST /projects when you supply a builder_id and form_data (see below).


Projects

Create project

POST /projects

Creates a project from a strict builder payload and starts builder execution asynchronously shortly after creation.

Request body

Field Type Description
builder_id string Builder identifier (e.g. google_map_builder).
form_data object Fully filled builder form (same shape as the product submission).

Example

import requests
import json

url = "https://api.kurationai.com/api/enterprise/projects"

headers = {
    "accept": "application/json",
    "Content-Type": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

data = {
    "builder_id": "google_map_builder",
    "form_data": {
        "query": "seed stage fintech companies in sf",
        "max_results": 50,
    },
}

response = requests.post(url, headers=headers, json=data)
print(response.status_code)
print(json.dumps(response.json(), indent=2))
curl -sS -X POST "https://api.kurationai.com/api/enterprise/projects" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -H "kur-api-key: YOUR_API_KEY" \
  -d '{"builder_id":"google_map_builder","form_data":{"query":"seed stage fintech companies in sf","max_results":50}}'
const url = "https://api.kurationai.com/api/enterprise/projects";

const response = await fetch(url, {
  method: "POST",
  headers: {
    accept: "application/json",
    "Content-Type": "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
  body: JSON.stringify({
    builder_id: "google_map_builder",
    form_data: {
      query: "seed stage fintech companies in sf",
      max_results: 50,
    },
  }),
});

console.log(response.status, await response.json());

Response (201/200 on success — follow status_code from your client)

{
  "project_id": "9d6f0b7b-..."
}

Errors

HTTP Meaning
400 Invalid builder_id / form_data (validation).
500 Unexpected failure creating or kicking off the build.

Try this request


List projects

GET /projects

Returns projects your API user can access, ordered by updatedAt descending. The response wraps a projects array plus pagination metadata (not a bare JSON array).

Query parameters

Param Type Default Constraints Description
limit int 50 1–200 Page size.
offset int 0 ≥ 0 Rows to skip (ignored if page is set).
page int (unset) ≥ 1 1-based page; when set, offset is computed as (page - 1) * limit.
name_search string (unset) Case-insensitive substring filter on project name.

total is the full count after the name filter (not only the current page).

Minimal example (matches the in-app snippet)

import requests
import json

url = "https://api.kurationai.com/api/enterprise/projects"

headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

response = requests.get(url, headers=headers)
print(response.status_code)
print(json.dumps(response.json(), indent=2))
curl -sS "https://api.kurationai.com/api/enterprise/projects" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY"
const url = "https://api.kurationai.com/api/enterprise/projects";

const response = await fetch(url, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(response.status, await response.json());

Paginated example

import requests

url = "https://api.kurationai.com/api/enterprise/projects"
headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}
params = {"page": 2, "limit": 25, "name_search": "Q4"}
response = requests.get(url, headers=headers, params=params)
print(response.json())
curl -sS -G "https://api.kurationai.com/api/enterprise/projects" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY" \
  --data-urlencode "page=2" \
  --data-urlencode "limit=25" \
  --data-urlencode "name_search=Q4"
const base = "https://api.kurationai.com/api/enterprise/projects";
const params = new URLSearchParams({
  page: "2",
  limit: "25",
  name_search: "Q4",
});

const response = await fetch(`${base}?${params}`, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(response.status, await response.json());

Try this request (paginated query)

Response shape (illustrative)

{
  "projects": [
    {
      "id": "98e79cf8-87c9-49b0-9899-58edb6f3c98b",
      "name": "Tech Companies Q4",
      "creator_id": "user_abc123",
      "company_count": 150,
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-20T14:45:00Z"
    }
  ],
  "total": 42,
  "limit": 50,
  "offset": 0,
  "page": 1
}

Difference from shorthand examples

Some UI examples display only a JSON array of projects; the live API returns the object above including projects, total, limit, offset, and page.


Get project details

GET /projects/{project_id}

Returns column schema (for mapping inputs), first_company_data (sample row), row_count, and builder_status. The backend may also include lead_gen_execution_log for certain project types.

Example

import requests
import json

project_id = "YOUR_PROJECT_ID"
url = f"https://api.kurationai.com/api/enterprise/projects/{project_id}"

headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

response = requests.get(url, headers=headers)
print(response.status_code)
print(json.dumps(response.json(), indent=2))
curl -sS "https://api.kurationai.com/api/enterprise/projects/YOUR_PROJECT_ID" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY"
const projectId = "YOUR_PROJECT_ID";
const url = `https://api.kurationai.com/api/enterprise/projects/${projectId}`;

const response = await fetch(url, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(response.status, await response.json());

Try this request

Example response (shape)

{
  "columns": [
    {
      "col_id": "0674c9a3-8d34-4117-94ba-2e4e244e48c9",
      "name": "company_name",
      "required": true
    },
    {
      "col_id": "4905e83c-bbb5-462f-9917-73d2ed14727c",
      "name": "website",
      "required": true
    },
    {
      "col_id": "466527f1-ef79-4cd6-bf03-c878681da41b",
      "name": "industry",
      "required": false
    }
  ],
  "first_company_data": {
    "company_name": "Acme Corporation",
    "website": "https://acme.com",
    "industry": "Technology"
  },
  "row_count": 1,
  "builder_status": { "type": "completed", "error_message": null }
}

Errors

HTTP Meaning
404 Project missing or no access.

Rows

Save company data

POST /projects/{project_id}/rows

Body uses column names (not column IDs) as keys under company. All normal (non-tool) columns for the project must be present; tool columns are filled by Kuration.

Request body

{
  "company": {
    "company_name": "Acme Corporation",
    "website": "https://acme.com"
  }
}

Example

import requests

project_id = "YOUR_PROJECT_ID"
url = f"https://api.kurationai.com/api/enterprise/projects/{project_id}/rows"

headers = {
    "accept": "application/json",
    "Content-Type": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

data = {
    "company": {
        "company_name": "Acme Corporation",
        "website": "https://acme.com",
    }
}

response = requests.post(url, headers=headers, json=data)
print(response.status_code)
print(response.json())
curl -sS -X POST \
  "https://api.kurationai.com/api/enterprise/projects/YOUR_PROJECT_ID/rows" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -H "kur-api-key: YOUR_API_KEY" \
  -d '{"company":{"company_name":"Acme Corporation","website":"https://acme.com"}}'
const projectId = "YOUR_PROJECT_ID";
const url = `https://api.kurationai.com/api/enterprise/projects/${projectId}/rows`;

const response = await fetch(url, {
  method: "POST",
  headers: {
    accept: "application/json",
    "Content-Type": "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
  body: JSON.stringify({
    company: {
      company_name: "Acme Corporation",
      website: "https://acme.com",
    },
  }),
});

console.log(response.status, await response.json());

Try this request

Response

{
  "row_id": "Jxl8KwcI6bAHH8wdQRvs",
  "message": "Success",
  "error_detail": null
}

Tool columns may execute in the background after the row is created.

Errors

HTTP Meaning
404 Project missing or no access.
403 Missing required normal columns or invalid payload (see detail.missing_columns / detail.message).

List project rows

GET /projects/{project_id}/rows

Paginated listing with optional filtering and sorting.

Query parameters

Param Type Default Constraints Description
page int 1 ≥ 1 Page number.
page_size int 50 1–100 Rows per page.
filter_groups string JSON array URL-encoded Same conceptual schema as filterGroups on the project in the UI. Invalid JSON/shape → 422.
sort_rules string JSON array URL-encoded e.g. [{"col_id":"<uuid>","direction":"asc","sort_as_number":false}]. Unknown column → 422.

Basic example

import requests
import json

project_id = "YOUR_PROJECT_ID"
url = f"https://api.kurationai.com/api/enterprise/projects/{project_id}/rows"

headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

params = {"page": 1, "page_size": 50}
response = requests.get(url, headers=headers, params=params)
print(json.dumps(response.json(), indent=2))
curl -sS -G \
  "https://api.kurationai.com/api/enterprise/projects/YOUR_PROJECT_ID/rows" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY" \
  --data-urlencode "page=1" \
  --data-urlencode "page_size=50"
const projectId = "YOUR_PROJECT_ID";
const base = `https://api.kurationai.com/api/enterprise/projects/${projectId}/rows`;
const params = new URLSearchParams({ page: "1", page_size: "50" });

const response = await fetch(`${base}?${params}`, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(await response.json());

Try this request

Filter / sort example — Pass JSON as a single query string parameter (properly encoded).

import json
import requests

project_id = "YOUR_PROJECT_ID"
url = f"https://api.kurationai.com/api/enterprise/projects/{project_id}/rows"

headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

filter_groups = [{"type": "and", "rules": []}]  # replace with real filter groups from your project
sort_rules = [
    {"col_id": "4905e83c-bbb5-462f-9917-73d2ed14727c", "direction": "asc", "sort_as_number": False}
]

params = {
    "page": 1,
    "page_size": 25,
    "filter_groups": json.dumps(filter_groups, separators=(",", ":")),
    "sort_rules": json.dumps(sort_rules, separators=(",", ":")),
}
# requests will URL-encode values
response = requests.get(url, headers=headers, params=params)
# Compact JSON for query params (no spaces after ":")
FG='[{"type":"and","rules":[]}]'
SR='[{"col_id":"4905e83c-bbb5-462f-9917-73d2ed14727c","direction":"asc","sort_as_number":false}]'

curl -sS -G \
  "https://api.kurationai.com/api/enterprise/projects/YOUR_PROJECT_ID/rows" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY" \
  --data-urlencode "page=1" \
  --data-urlencode "page_size=25" \
  --data-urlencode "filter_groups=${FG}" \
  --data-urlencode "sort_rules=${SR}"
const projectId = "YOUR_PROJECT_ID";
const base = `https://api.kurationai.com/api/enterprise/projects/${projectId}/rows`;

const filterGroups = [{ type: "and", rules: [] }];
const sortRules = [
  {
    col_id: "4905e83c-bbb5-462f-9917-73d2ed14727c",
    direction: "asc",
    sort_as_number: false,
  },
];

const params = new URLSearchParams({
  page: "1",
  page_size: "25",
  filter_groups: JSON.stringify(filterGroups),
  sort_rules: JSON.stringify(sortRules),
});

const response = await fetch(`${base}?${params}`, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(await response.json());

Try this request (filter + sort)

Response shape (illustrative)

{
  "rows": [
    {
      "id": "4d9ace8e-bdcf-401f-93db-a4b73d12cf13",
      "project_id": "98e79cf8-87c9-49b0-9899-58edb6f3c98b",
      "company": {
        "company_name": {
          "name": "company_name",
          "value": "Acme Corporation",
          "status": "default",
          "is_loading": false
        },
        "website": {
          "name": "website",
          "value": "https://acme.com",
          "status": "default",
          "is_loading": false
        }
      }
    }
  ],
  "page": 1,
  "page_size": 50,
  "total_rows": 243,
  "total_pages": 5
}

Errors

HTTP Meaning
404 Project missing or no access.
422 Bad filter_groups or sort_rules payload.

Get row results

GET /projects/{project_id}/rows/{row_id}

Returns one row’s company map in the API’s structured format.

Example

import requests
import json

project_id = "YOUR_PROJECT_ID"
row_id = "YOUR_ROW_ID"
url = f"https://api.kurationai.com/api/enterprise/projects/{project_id}/rows/{row_id}"

headers = {
    "accept": "application/json",
    "kur-api-key": "YOUR_API_KEY",
}

response = requests.get(url, headers=headers)
print(response.status_code)
print(json.dumps(response.json(), indent=2))
curl -sS \
  "https://api.kurationai.com/api/enterprise/projects/YOUR_PROJECT_ID/rows/YOUR_ROW_ID" \
  -H "accept: application/json" \
  -H "kur-api-key: YOUR_API_KEY"
const projectId = "YOUR_PROJECT_ID";
const rowId = "YOUR_ROW_ID";
const url =
  `https://api.kurationai.com/api/enterprise/projects/${projectId}/rows/${rowId}`;

const response = await fetch(url, {
  headers: {
    accept: "application/json",
    "kur-api-key": "YOUR_API_KEY",
  },
});

console.log(response.status, await response.json());

Try this request

Example response

{
  "id": "4d9ace8e-bdcf-401f-93db-a4b73d12cf13",
  "project_id": "98e79cf8-87c9-49b0-9899-58edb6f3c98b",
  "company": {
    "company_name": {
      "name": "company_name",
      "value": "Acme Corporation",
      "status": "default",
      "is_loading": false
    },
    "website": {
      "name": "website",
      "value": "https://acme.com",
      "status": "default",
      "is_loading": false
    }
  }
}

Errors

HTTP Meaning
404 Project/row missing, no access, or row deleted.

Source of truth

Server routes for this surface live in enterprise_v3_router.py (GET/POST /projects, row endpoints). When behavior or payloads change, update this page and the in-app integration copy together.