Import / Export
Import records in batch and export data as CSV or JSON. Admin-only endpoints for data management.
The import and export endpoints allow administrators to move data in and out of Kasar in bulk. Both endpoints require admin privileges.
Import Records
POST /api/v1/import/{object}Batch import records into an object in a single call. Each record is created through the standard object service, so pipeline defaults (pipeline, step, and step-field defaults) are automatically applied for objects with a pipeline. Records are created sequentially. A record whose creation hits a unique-constraint violation is counted as a duplicate rather than failing the request.
This endpoint is restricted to admin users. Non-admin tokens receive a PERMISSION_DENIED error with HTTP status 400.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
object | string | The object to import into (e.g. contacts, companies) |
Request Body
{
"records": [
{
"first_name": "Alice",
"last_name": "Martin",
"email": "alice@example.com",
"company_name": "Acme Corp"
},
{
"first_name": "Bob",
"last_name": "Dupont",
"email": "bob@startup.io"
}
]
}| Field | Type | Description |
|---|---|---|
records | object[] | Required, non-empty. Array of records to import. Each record is a key-value map matching the object's field names. |
add_to_list_ids | string[] | Optional. Adds every created record to these existing lists (per-list outcome in lists). Duplicates and failed rows are excluded. |
create_list_name | string | Optional. Creates a new list with this name and adds every created record to it — import + list in one call. |
Limits
- Max duration: 300 seconds (5 minutes)
Example Request
curl -X POST "https://kasar.app/api/v1/import/contacts" \
-H "Authorization: Bearer ksr_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"first_name": "Alice",
"last_name": "Martin",
"email": "alice@example.com"
},
{
"first_name": "Bob",
"last_name": "Dupont",
"email": "bob@startup.io"
}
]
}'Response
{
"object_name": "contacts",
"created": 1,
"created_ids": ["b1f2c3d4-0000-4a5b-8c9d-1e2f3a4b5c6d"],
"duplicates": 1,
"failed": 0,
"errors": []
}| Field | Type | Description |
|---|---|---|
object_name | string | The object that was imported into |
created | integer | Number of records successfully created |
created_ids | string[] | IDs of every created record |
duplicates | integer | Number of records skipped because creation hit a unique-constraint violation |
failed | integer | Number of records that failed for any other reason |
errors | object[] | Details for each failed record, up to 100 entries: { index, error } |
When add_to_list_ids or create_list_name is supplied and at least one record was created, the response also includes list-membership fields:
| Field | Type | Description |
|---|---|---|
lists | object[] | Per-list outcome for the created records that were added |
created_list | object | Present when create_list_name succeeded — the newly created list |
create_list_error | string | Present when create_list_name could not be created |
Duplicate Detection
A record counts as a duplicate only when its insert violates a unique constraint on the object (PostgreSQL error 23505, e.g. a unique email). These rows are not created and are counted in the duplicates field — they are not included in created_ids and are excluded from add_to_list_ids / create_list_name.
Error Example
When some records fail for a non-duplicate reason (validation, missing relation, etc.):
{
"object_name": "contacts",
"created": 1,
"created_ids": ["b1f2c3d4-0000-4a5b-8c9d-1e2f3a4b5c6d"],
"duplicates": 0,
"failed": 1,
"errors": [
{
"index": 1,
"error": "Field 'email' must be a valid email address"
}
]
}Each entry in errors is { index, error } — the zero-based index of the record in the request and the error message. There is no per-row code field.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | PERMISSION_DENIED | Token does not belong to an admin user, or the caller lacks create permission on the object |
| 400 | INVALID_OBJECT | The object name does not exist |
| 400 | MISSING_REQUIRED_FIELD | The records array is missing or empty |
| 401 | UNAUTHORIZED | Invalid or missing Bearer token |
Export Records
GET /api/v1/export/{object}Export records from an object as JSON or CSV. Supports filtering, sorting, and field selection. Record references (relation / rollup UUIDs) are resolved to their display value before export — raw UUIDs are never returned.
This endpoint is restricted to admin users. Non-admin tokens receive a PERMISSION_DENIED error with HTTP status 400.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
object | string | The object to export (e.g. contacts, companies) |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format | string | json | Export format: json or csv |
fields | string | all fields | Comma-separated list of field names to include. When omitted, internal/noise columns (search_vector, cached_data, raw_metadata, raw_data) are stripped; an explicit fields request is honoured verbatim. |
filter_field | string | Field name for a single inline filter | |
filter_operator | string | Filter operator. Use the internal operator names, e.g. equals, not_equals, contains, in, greater_than, less_than, greater_equal, less_equal, is_null, is_not_null. With in, pass a comma-separated filter_value. | |
filter_value | string | Filter value for filter_field / filter_operator | |
filters | string | Advanced filters as a JSON-encoded FilterGroup object. When both filter_field and filters are supplied, they are combined with AND. | |
sort_by | string | Field to sort by. When omitted, no explicit ordering is applied. | |
sort_dir | string | asc | Sort direction: asc or desc (only used when sort_by is set) |
limit | integer | 5000 | Maximum records to export (capped at 10000) |
Example: Export as JSON
curl -X GET "https://kasar.app/api/v1/export/contacts?format=json&fields=first_name,last_name,email&limit=500" \
-H "Authorization: Bearer ksr_a1b2c3d4e5f6..."Response:
{
"format": "json",
"records": [
{
"first_name": "Alice",
"last_name": "Martin",
"email": "alice@example.com"
},
{
"first_name": "Bob",
"last_name": "Dupont",
"email": "bob@startup.io"
}
],
"total": 2
}| Field | Type | Description |
|---|---|---|
format | string | Always json for this response |
records | object[] | The exported records |
total | integer | Number of records returned |
Example: Export as CSV
curl -X GET "https://kasar.app/api/v1/export/contacts?format=csv&fields=first_name,last_name,email" \
-H "Authorization: Bearer ksr_a1b2c3d4e5f6..." \
-o contacts.csvWhen format=csv, the response body is the raw CSV text (not a JSON envelope) with:
Content-Type: text/csv; charset=utf-8Content-Disposition: attachment; filename="contacts_export.csv"(filename is{object}_export.csv)
Cell values are flattened to a single readable string: relation/display objects render their display label, CURRENCY renders amount currency_code, ADDRESS renders its formatted value, and arrays (M2M relations, MULTI_SELECT) are joined with ; .
Example: Export with Filters
curl -X GET "https://kasar.app/api/v1/export/contacts?format=json&filter_field=status&filter_operator=equals&filter_value=qualified&sort_by=last_name&sort_dir=asc" \
-H "Authorization: Bearer ksr_a1b2c3d4e5f6..."For complex filters, use the filters parameter with a JSON-encoded FilterGroup:
curl -G "https://kasar.app/api/v1/export/contacts" \
-H "Authorization: Bearer ksr_a1b2c3d4e5f6..." \
--data-urlencode 'format=json' \
--data-urlencode 'filters={"type":"AND","conditions":[{"field":"status","operator":"equals","value":"qualified"},{"field":"created_at","operator":"date_after","value":"2025-01-01"}]}'A FilterGroup has type ("AND" or "OR") and a conditions array of { field, operator, value } conditions (or nested groups). Invalid JSON in filters is ignored.
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | PERMISSION_DENIED | Token does not belong to an admin user |
| 400 | INVALID_OBJECT | The object name does not exist or is not readable |
| 401 | UNAUTHORIZED | Invalid or missing Bearer token |