Skip to content

Loads


8. Loads

A load adds rows to one lake table. Four steps:

  1. Create the load with POST /load. You get a loadId.
  2. Upload one or more parquet files with POST /load/{load_id}/file, then upload each file to the link it returns.
  3. Commit the load with checksums using POST /load/{load_id}/commit.
  4. Poll GET /load/{load_id} until SUCCESS or FAIL.

Either all rows are added or none. Only you can see or change your loads.

8.1 Load status

Status Meaning
OPEN Created, not committed. You can still upload files.
PROCESSING Committed and being validated.
SUCCESS All rows were written to the lake table.
FAIL Nothing was written. ErrorType and ErrorMessages say why.

Poll every 5 seconds while the status is PROCESSING.

8.2 POST /load: create a load

URL: POST https://<org>.api.comodash.io/v2/load | Scope: load:write

SDK equivalent: Load(config, load_type="APPEND_ONLY", table_name=...)

Request fields

Field Type Required Description
load_type string yes Must be APPEND_ONLY. Rows are added, never changed or removed.
table_name string yes Target lake table. Lowercase letters, numbers and underscores. Cannot start or end with underscore. Created on first load.
load_as_service_client_id string no All rows belong to this service client. Files must not then have a service_client_id column. Send as a string, e.g. "12".
partitions string[] no Extra partition columns. Up to 100 distinct value combinations per load.

Response 202

{"loadId": "20261013095801_355d42f6_0684_4b8f_b274_225bf7237494"}

Key errors: 400 MissingParameterError for missing fields; 400 InvalidParameterError: invalid load_type parameter (only APPEND_ONLY is accepted); 400 InvalidParameterError: table_name - Value should be in lowercase and contain only lowercase letters and underscores.

load_as_service_client_id is not verified here. If you cannot access that service client, the commit fails later with ErrorType: PermissionsError.

LOAD_ID=$(curl -s -X POST "$API/load" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"load_type":"APPEND_ONLY","table_name":"policies","load_as_service_client_id":"12"}' \
  | jq -r .loadId)
r = requests.post(f"{API}/load", headers=HEADERS, json={
    "load_type": "APPEND_ONLY", "table_name": "policies",
    "load_as_service_client_id": "12",
})
r.raise_for_status()
load_id = r.json()["loadId"]

8.3 POST /load/{load_id}/file: upload a file

Returns a pre-signed S3 URL and temporary credentials for uploading one parquet file. Call once per file. The credentials expire after 1 hour.

URL: POST https://<org>.api.comodash.io/v2/load/{load_id}/file | Scope: load:write

SDK equivalent: Load.upload_file(data, ...) or Load.upload_df(df, ...)

Optional request body field

Field Type Description
file_key string Custom filename stem. Lowercase, letters, numbers, underscores. Uploading to the same file_key twice overwrites the first file (idempotent).

Response 200

Field Type Description
presigned_url object Use as an HTTP POST to S3. Contains url and fields (AWSAccessKeyId, Policy, Signature, key, x-amz-security-token).
sts_credentials object Temporary AWS credentials: AccessKeyId, SecretAccessKey, SessionToken. The SDK uses these with boto3.
path string S3 object key.
bucket string S3 bucket name.

File requirements (enforced at commit time, not during upload):

  • Must be a valid parquet file.
  • Column names: only lowercase letters, numbers and underscores.
  • If load_as_service_client_id was not given in POST /load, files must include a service_client_id column.
  • If load_as_service_client_id was given, files must not include a service_client_id column.

Key errors: 400 InvalidParameterError: file_key - ... for a bad file_key; 404 NotFound: load_id does not exist for this user. for an unknown load ID.

8.4 POST /load/{load_id}/commit: commit the load

Starts the load pipeline. The status becomes PROCESSING. The load ID is the pipeline execution name, so calling this endpoint twice returns 400 InvalidParameter: Load Already Committed.

URL: POST https://<org>.api.comodash.io/v2/load/{load_id}/commit | Scope: load:write

SDK equivalent: Load.commit(check_sum={...})

Request body

{"check_sum": {"count(*)": 50000, "sum(face_amount)": 9876543210}}

check_sum is required and must have at least one entry. Keys are SQL aggregate expressions run over all uploaded rows. Values are what you expect. Accepts string, integer or float values.

Response 202: {"message": "Commit initiated."}

Key errors

Status Message Cause
400 Invalid checksum data provided. check_sum is not an object or has wrong types.
400 MissingParameter: missing check_sum parameter. At least one check_sum is required The check_sum object is empty.
400 InvalidParameter: Load Already Committed. Already committed. Check GET /load/{load_id}.
404 NotFound: load_id does not exist for this user. Wrong load ID or not created by you.
curl -s -X POST "$API/load/$LOAD_ID/commit" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"check_sum": {"count(*)": 50000}}'
requests.post(f"{API}/load/{load_id}/commit", headers=HEADERS,
              json={"check_sum": {"count(*)": 50000}}).raise_for_status()

8.5 GET /load/{load_id}: get load status

URL: GET https://<org>.api.comodash.io/v2/load/{load_id} | Scope: load:read

SDK equivalent: Load.wait_to_complete(), Load.get_load_info()

Response 200

Field Type Description
LoadStatus string OPEN, PROCESSING, SUCCESS or FAIL.
ErrorType string Only present on FAIL.
ErrorMessages array Only present on FAIL. Items can be strings or objects with ErrorType, ErrorMessage and CheckResult.

ErrorType values

ErrorType Cause
NoFilesUploaded No files were uploaded before the commit.
ParquetValidation File is not valid parquet, or has column names with disallowed characters.
ServiceClientError service_client_id is missing from both files and load_as_service_client_id, or present in both, or a row belongs to a service client you cannot access.
CheckSumError An aggregate value does not match your expected value. The message names the aggregate, expected and actual.
ValidationError Columns do not match the existing lake table (type mismatch, extra or missing columns).
TooManyPartitions More than 100 distinct partition value combinations.
PermissionsError load_as_service_client_id is not a service client you can access.
InternalError Unexpected internal error. Contact support.
{
  "LoadStatus": "FAIL",
  "ErrorType": "CheckSumError",
  "ErrorMessages": [
    {
      "CheckResult": "FAIL",
      "ErrorType": "CheckSumError",
      "ErrorMessage": "Checksum count(*) was expecting 50001 and got 50000"
    }
  ]
}
import time

while True:
    r = requests.get(f"{API}/load/{load_id}", headers=HEADERS).json()
    print(r["LoadStatus"])
    if r["LoadStatus"] not in ("OPEN", "PROCESSING"):
        break
    time.sleep(5)
if r["LoadStatus"] != "SUCCESS":
    raise RuntimeError(r.get("ErrorMessages"))

8.6 What happens after commit (pipeline overview)

  1. Files are collected and validated (parquet format and column names).
  2. A temporary Athena table is created from all uploaded files.
  3. Service client rules are enforced per row.
  4. Checksums are calculated and compared with your expected values.
  5. The lake table is created (if new) or columns are matched (if it exists). Column types must match.
  6. Rows are inserted. Every row gets a dash$load_id column. The table is partitioned by service_client_id, dash$load_id and any extra partitions you requested.