Skip to main content

Records

Send JSON records with your own field names:
A purchase order must carry the order number, the customer’s name, and the order date. An invoice must carry the invoice number, the invoice date, and the total amount. Dates in common formats are normalized. Unparseable required dates are reported as missing. Ids are unique across the datasource. An invoice and a purchase order sharing a source number need different ids. An upload may contain at most 20 distinct combinations of document type and record layout.

Open an upload

Send POST /uploads:
Choose incremental or full using the mode table. The 201 response includes upload_id, status: "in_progress", mode, dry_run, datasource, and created_at. Save the upload id. An in_progress upload is writable while closed_at is null. Opening another upload expires the previous writable upload and discards its staged records. If the current upload has closed_at set, opening returns 409 upload_processing with Retry-After: 30.

Send records

Send batches to PUT /uploads/{upload_id}/records:
Each call allows at most 1,000 records and 10 MB (10,485,760 bytes). The entire batch is validated before any write. A rejected batch stages nothing. Invalid ids or undeclared document types return 400 invalid_record. Its details.records entries contain index, id, and reason: invalid_id or undeclared_doc_type. A non-object data value returns 400 invalid_request. Sending the same id again within an upload replaces its staged value: last write wins. Retrying the same records is safe. The response contains accepted (records accepted in this batch), upload_id, and record_count (distinct staged ids).

Close

Call POST /uploads/{upload_id}/close. Close sets closed_at and returns 202 with upload_id and status. Processing is asynchronous. Further writes are rejected. Repeated closes return the current status, except expired uploads return 410 upload_expired. If scheduling fails, close returns 503 publish_failed with Retry-After: 5. Wait five seconds and close again to retry scheduling.

Poll

Call GET /uploads/{upload_id}. While the upload is in_progress with closed_at set, honor Retry-After: 15. The response includes record count and timestamps. merged_counts holds inserted, updated, and deleted after a successful merge. report is null until validation finishes; then it contains errors and per-document-type findings. Rejected or failed uploads carry an error envelope. See Errors and recovery. The hourly sweep expires writable uploads after at least 24 hours without a write. Worker failures leave closed uploads in_progress during retries. After at least one hour of processing, the stuck-upload sweep marks them failed with processing_timeout. Rejected, failed, and expired uploads have their staging discarded.

Full snapshots and the deletion brake

full upserts submitted records and removes current records absent from the upload. With a non-empty current set, removing more than 20% or submitting zero records rejects the upload with too_many_deletions. Nothing changes. Error details include current_count, would_delete, and max_delete_fraction. The 20% threshold is fixed across the deployment. Resend the intended full set, and contact Pascal if a larger removal is intentional.

Dry runs

Set dry_run: true when opening. Close validates the records and returns done or rejected with a report. It discards staging and leaves merged_counts null. Documents and the last accepted upload remain unchanged.

Inspect the datasource

Call GET /datasources/{name} with your bearer token. The name must match the token’s datasource, or the response is 403 datasource_mismatch.