API & Webhook Documentation
Programmatic access to Kuration's data enrichment and list-building platform. Submit data, trigger enrichments, and receive results in real time.
Overview
Kuration provides two integration methods for programmatic access to our data enrichment and list-building platform:
Project Workflow API
Submit company data, trigger enrichment tools, and retrieve results via REST endpoints.
Webhooks
Receive real-time notifications when enrichment tasks complete — no polling required.
Base URL
https://api.kurationai.com/api/enterpriseAuthentication
All API requests require your API key in the request header:
kur-api-key: YOUR_API_KEYContact us to receive your API key. API access is available on the Enterprise plan ($1,000/month).
Project Workflow API
The workflow follows three steps: submit company data, receive a row ID, then fetch enriched results.
Submit Company Data
Send a POST request with company information to a project
Receive Row ID
The API returns a row_id to track your submission
Fetch Results
Use project_id and row_id to retrieve the enriched data
/projectsList All Projects
Retrieve all projects accessible to your account.
Request
import requests
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.json())Response
[
{
"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"
}
]/projects/{id}Get Project Details
Retrieve columns schema and sample data for a specific project.
Request
import requests
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.json())Response
{
"columns": [
{ "col_id": "0674c9a3-...", "name": "company_name", "required": true },
{ "col_id": "4905e83c-...", "name": "website", "required": true },
{ "col_id": "466527f1-...", "name": "industry", "required": false }
],
"first_company_data": {
"company_name": "Acme Corporation",
"website": "https://acme.com",
"industry": "Technology"
}
}/projects/{id}/rowsSave Company Data
Submit company information to a project for enrichment.
Request
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.json())Response
{
"row_id": "Jxl8KwcI6bAHH8wdQRvs",
"message": "Success",
"error_detail": null
}/projects/{id}/rowsGet All Rows
Retrieve all rows in a project with pagination. Use page and page_size query parameters to control results.
Request
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))Response
{
"rows": [
{
"id": "Jxl8KwcI6bAHH8wdQRvs",
"company": {
"company_name": { "value": "Acme Corporation", "status": "default" },
"website": { "value": "https://acme.com", "status": "default" }
}
}
],
"page": 1,
"page_size": 50,
"total": 150
}Use pagination to efficiently retrieve large datasets. The default page_size is 50. Maximum page_size is 200.
/projects/{id}/rows/{row_id}Get Row Results
Retrieve enriched company data after processing completes.
Request
import requests
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.json())Response
{
"id": "4d9ace8e-bdcf-401f-93db-a4b73d12cf13",
"project_id": "98e79cf8-87c9-49b0-...",
"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
}
}
}When is_loading is true, the enrichment tool is still processing. Poll again after a short delay, or use webhooks for real-time notifications.
Webhooks
Webhooks notify your server in real-time when an enrichment tool completes processing. This eliminates the need to poll the API for results.
Payload Structure
JSON payload sent via POST to your webhook URL when a tool cell completes.
Headers
Example Payload
{
"event": "tool_output_ready",
"project_id": "98e79cf8-87c9-49b0-9899-58edb6f312345",
"row_id": "Jxl8KwcI6bAHH8wdQ123",
"col_id": "466527f1-ef79-4cd6-bf03-c878681d1234",
"value": "Technology",
"status": "success",
"timestamp": "2024-01-20T14:45:00.000Z"
}Field Descriptions
| Field | Type | Description |
|---|---|---|
| event | string | Event type (always "tool_output_ready") |
| project_id | string | UUID of the project |
| row_id | string | ID of the row that was processed |
| col_id | string | UUID of the tool column |
| value | any | The result value from the tool |
| status | string | "success" or "error" |
| timestamp | string | ISO 8601 timestamp |
Webhook Handler Example
Complete Flask server to receive and verify Kuration webhooks.
from flask import Flask, request, jsonify
import hmac, hashlib
app = Flask(__name__)
WEBHOOK_SECRET = "YOUR_WEBHOOK_SECRET"
@app.route("/webhook", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-Kuration-Signature")
if not signature:
return jsonify({"error": "Missing signature"}), 401
payload = request.get_data()
expected = hmac.new(
WEBHOOK_SECRET.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return jsonify({"error": "Invalid signature"}), 401
data = request.json
# Process: data["event"], data["row_id"], data["value"], etc.
return jsonify({"success": True}), 200
if __name__ == "__main__":
app.run(port=5000)Signature Verification
Verify webhook authenticity using HMAC-SHA256.
import hmac, hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Security Best Practices
Always verify the HMAC signature before processing webhook payloads
Use constant-time comparison (hmac.compare_digest) to prevent timing attacks
Store your API key and webhook secret in environment variables — never hardcode them
Respond to webhooks within 5 seconds to avoid timeouts
Use HTTPS for your webhook endpoint
Need Help?
For integration support, reach out to us directly. We're happy to help with setup, testing, and debugging.