Imports & Exports API
Bulk data movement in and out of your workspace: import subscribers into a list from a CSV file (with presets for Klaviyo and Braze exports), and export your behavioral event history.
Authentication
The two surfaces use different credentials:
-
Subscriber imports are authenticated with a bearer token, like the dashboard APIs:
Authorization: Bearer YOUR_ACCESS_TOKEN -
Event export requires a server API key (
hober_srv_…) — see GET /v1/events/export below.
Subscriber imports
Imports run as asynchronous jobs: upload a CSV, get a job ID back
immediately, then poll the job until it completes. Rows that fail validation
are counted in failed_rows without aborting the rest of the import.
Import job statuses
| Status | Meaning |
|---|---|
pending | Accepted, waiting to be picked up |
processing | Rows are being processed |
completed | All rows attempted (terminal) |
failed | The job hit an unrecoverable error (terminal) — see error_message |
POST /api/v1/lists/import
Starts a subscriber import into an existing list. The request is
multipart/form-data.
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
listId | string | Yes | UUID of the target subscriber list. |
file | file | Yes | The CSV file. Must have a text/csv content type or a .csv extension. Maximum size 50 MB. |
source | string | No | Import preset: csv (default), klaviyo, or braze. |
Import presets
The preset controls how the CSV's columns are interpreted:
csv— the standard column contract (default whensourceis omitted).klaviyo— accepts a Klaviyo profile export as-is: Klaviyo's headers are aliased to the standard fields, extra columns are packed into subscriber attributes, and Klaviyo consent statuses are translated conservatively.braze— accepts a Braze user export as-is, with the equivalent header mapping.
Response — 202 Accepted
{
"import_job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}
Error responses
| Status | Meaning |
|---|---|
400 | Missing listId or file, or the file is not a CSV |
401 | Missing or invalid bearer token |
404 | The list does not exist in your workspace |
413 | File larger than 50 MB |
422 | Unknown source value |
GET /api/v1/lists/import/:jobId
Fetches the status of an import job.
Response — 200 OK
{
"import_job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"processed_rows": 1500,
"total_rows": 5000,
"failed_rows": 10
}
| Field | Type | Description |
|---|---|---|
import_job_id | string | The job UUID. |
status | string | pending, processing, completed, or failed. |
processed_rows | int | Rows attempted so far. |
total_rows | int | Total rows in the file. Absent until processing starts. |
failed_rows | int | Rows that failed validation and were skipped. |
error_message | string | Terminal error reason. Present only on failed jobs. |
404 — no import job with that ID in your workspace.
Event exports
GET /v1/events/export
Bulk export of the workspace's behavioral event history, paginated with an opaque cursor. This endpoint requires a server API key — publishable client SDK keys cannot read event history:
Authorization: Bearer hober_srv_YOUR_SERVER_KEY
This is the same endpoint documented on the Events page — see there for the event field semantics and ingestion counterpart.
Query parameters
| Param | Type | Required | Description |
|---|---|---|---|
from | RFC 3339 | No | Window start on occurred_at. Defaults to all history. |
to | RFC 3339 | No | Window end. Defaults to now. |
cursor | string | No | Opaque cursor from the previous page's next_cursor. |
limit | int | No | Events per page, 1–1000. Defaults to 1000. |
Response — 200 OK
{
"items": [
{
"id": "6a7b8c9d-0e1f-2a3b-4c5d-6e7f8a9b0c1d",
"subscriber_id": "8a2f6c1e-1234-4abc-9def-567890abcdef",
"anonymous_id": null,
"event_name": "order_placed",
"platform": "web",
"properties": { "total_price": "19.99" },
"occurred_at": "2026-07-01T12:00:00Z",
"received_at": "2026-07-01T12:00:01Z",
"source": "server"
}
],
"next_cursor": ""
}
Rows are ordered by (occurred_at, id), so pages are stable under
concurrent ingestion. Keep requesting with the returned next_cursor until
it comes back empty — an empty next_cursor means the final page.
Error responses
| Status | Meaning |
|---|---|
401 | Missing, malformed, or revoked credential |
403 | The credential is a client SDK key — export requires a server key |
422 | Malformed from / to, limit out of range, or malformed cursor |