ingest-datahub-events-csv
dci ingest-datahub-events-csv [body]
Sends a batch of events to DataHub using a CSV file, either uncompressed or compressed in ZIP or GZ format. It may take up to 15 minutes for the data to become available in the DoiT console.
Pass the request body as name: value arguments or pipe JSON on stdin — see Command structure.
provider is the dataset name (dci list-datahub-datasets); create it
first with dci create-datahub-dataset name: <dataset> if it does not
exist. The CSV must follow the dataset's schema template. For the Default
template the header row is usage_date[,id],<dimension>...,<metric>...
with RFC 3339 UTC timestamps; see
CSV ingestion.
Requires the DataHub Admin permission. Rows appear in Cloud Analytics
within about 15 minutes.
There is no delete-by-batch API. Remove the rows with
dci delete-datahub-events-by-filter using the time range the file
covered, then upload the corrected file.
Examples
# Upload a CSV into the dataset `litellm-usage`; the `@` attaches the file and keeps its name.
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv
batch batchId generatedEvents ingestedRows provider
csv_events.csv_1756987200000 csv_events.csv_1756987200000 15 15 litellm-usage
# Upload a compressed export. Files over 30 MB must be gzipped or zipped first (one CSV per archive).
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv.gz
# Script-friendly result, scoped to another customer's tenant.
dci ingest-datahub-events-csv provider: litellm-usage, file: @events.csv --output json -D acme.com
{"batch": "csv_events.csv_1756987200000", "ingestedRows": 15, "generatedEvents": 15, "sourceRecords": 15, "provider": "litellm-usage", "schemaTemplate": "default@1"}
Request
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | The identifier of the data provider. Allowed characters: alphanumeric (0-9,a-z,A-Z), underscore (_), space, dash (-). | |
file | string (binary) | The CSV file to upload, either uncompressed or compressed in ZIP or GZ format. The maximum file size is 30 MB. | |
schemaTemplate | string | Versioned schema template of the file. Defaults to the dataset's template (default@1 for datasets created without one). Provider-native templates such as anthropic-spend-report@1 and codex-usage-report@1 validate the vendor's own columns and normalize them into DataHub events. Must match the template stored on the dataset. | |
periodStart | string (date) | Inclusive first day of the report period (YYYY-MM-DD). Required for template uploads whose filename does not carry the period; when both are present they must agree. | |
periodEnd | string (date) | Inclusive last day of the report period (YYYY-MM-DD). Events are booked on this day. | |
replace | boolean | Template uploads only. When true, active batches whose report period overlaps this upload are soft-deleted before the new file is ingested, and their ids are returned in replacedBatches. Refused with 409 when an overlapping batch extends outside the requested period (so a one-day replacement can never remove a wider upload) or was ingested less than 90 minutes ago (Retry-After header set). Without replace, any overlap is rejected with 409. Default: false. |
Raw JSON schema
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"description": "The identifier of the data provider. Allowed characters: alphanumeric (0-9,a-z,A-Z), underscore (_), space, dash (-).",
"example": "Datadog"
},
"file": {
"type": "string",
"format": "binary",
"description": "The CSV file to upload, either uncompressed or compressed in ZIP or GZ format. The maximum file size is 30 MB."
},
"schemaTemplate": {
"type": "string",
"description": "Versioned schema template of the file. Defaults to the dataset's template (`default@1` for datasets created without one). Provider-native templates such as `anthropic-spend-report@1` and `codex-usage-report@1` validate the vendor's own columns and normalize them into DataHub events. Must match the template stored on the dataset.",
"example": "anthropic-spend-report@1"
},
"periodStart": {
"type": "string",
"format": "date",
"description": "Inclusive first day of the report period (YYYY-MM-DD). Required for template uploads whose filename does not carry the period; when both are present they must agree.",
"example": "2026-09-10"
},
"periodEnd": {
"type": "string",
"format": "date",
"description": "Inclusive last day of the report period (YYYY-MM-DD). Events are booked on this day.",
"example": "2026-09-10"
},
"replace": {
"type": "boolean",
"default": false,
"description": "Template uploads only. When `true`, active batches whose report period overlaps this upload are soft-deleted before the new file is ingested, and their ids are returned in `replacedBatches`. Refused with `409` when an overlapping batch extends outside the requested period (so a one-day replacement can never remove a wider upload) or was ingested less than 90 minutes ago (`Retry-After` header set). Without `replace`, any overlap is rejected with `409`."
}
}
}
Output
OK - Ingestion succeeded.
By default dci renders the result as a table. Use --output json to get the full structure described below — see Output formats.
| Field | Type | Description |
|---|---|---|
batch | string | batch id, generated during ingestion |
ingestedRows | integer | the number of events that have been accepted for processing |
schemaTemplate | string | the schema template the file was validated against |
sourceRecords | integer | the number of source rows in the file |
generatedEvents | integer | the number of DataHub events generated from the source rows |
replacedBatches | array of string | ids of the batches soft-deleted because replace was set; omitted when none |
Raw JSON schema
{
"type": "object",
"properties": {
"batch": {
"type": "string",
"description": "batch id, generated during ingestion",
"example": "your_file.csv.gz_1730972725212"
},
"ingestedRows": {
"type": "integer",
"description": "the number of events that have been accepted for processing",
"example": 15
},
"schemaTemplate": {
"type": "string",
"description": "the schema template the file was validated against",
"example": "anthropic-spend-report@1"
},
"sourceRecords": {
"type": "integer",
"description": "the number of source rows in the file",
"example": 5
},
"generatedEvents": {
"type": "integer",
"description": "the number of DataHub events generated from the source rows",
"example": 15
},
"replacedBatches": {
"type": "array",
"description": "ids of the batches soft-deleted because `replace` was set; omitted when none",
"items": {
"type": "string"
},
"example": [
"csv_spend-report-…-2026-09-10-to-2026-09-10.csv_1757894400000"
]
}
}
}
Errors
On failure, dci prints a single error message — with a hint when one is available — and exits with a typed code your scripts can branch on. See Errors and exit codes for the full contract.
HTTP status to exit code mapping
| HTTP status | Exit code | Error code | Meaning |
|---|---|---|---|
| 400 | 30 | VALIDATION_ERROR | The arguments or request body were rejected. Review the command's flags and payload. |
| 401 | 10 | AUTHENTICATION_FAILED | Not signed in, or the API token is invalid. Run dci login or check DCI_API_KEY. |
| 403 | 11 | PERMISSION_DENIED | The DoiT user or the active customer context does not have access. |
| 404 | 20 | RESOURCE_NOT_FOUND | The requested resource does not exist. Check the identifier argument. |
| 409 | 21 | RESOURCE_CONFLICT | The operation conflicts with the resource's current state. |
| 500 | 40 | API_SERVER_ERROR | The API failed to process the request. Retryable; contact DoiT support if it persists. |
Related
- ingest-datahub-events — Ingest JSON
- create-datahub-dataset — Create dataset
- list-datahub-datasets — List datasets
- delete-datahub-events-by-filter — Delete specific events
- API reference: POST /datahub/v1/csv/upload
Aliases: datahub-events-csv-file, datahub-events-csvfile