Try Opteryx

Upload API

Base URL: https://upload.opteryx.app

Overview

Agree the column types before any data is sent, then upload files against that agreement and commit them as one snapshot.

Upload flow

An upload is an agreement, not a transfer. You send a sample of each file and the service answers with what the data will become; you read that, accept it, and only then does any data move. Everything that can refuse an upload — a type that will not cast, two files that disagree, a column the dataset does not declare, a permission you do not have — is decided while the cost is a few megabytes rather than the whole export.

Client Upload API
  1. 1 POST /v2/contracts
  2. 2 201 · contract_id, state, resolved mode, plan[], issues[], values
    1. PATCH /v2/contracts/{contract_id}
    2. 200 · the whole contract, re-planned
    3. PUT /v2/contracts/{contract_id}/accept
    4. 200 · state: accepted
    Only inference proposes. A declared schema, or an existing dataset's, arrives accepted
    1. POST /v2/contracts/{contract_id}/data
    2. 200 · written[] — what the file turned out to be
    3. 409 · a value that cannot be stored as the column it was promised to, with its row
  3. 3 POST /v2/contracts/{contract_id}/commit
  4. 4 200 · snapshot, rows_written
  • Negotiating uploads nothing. POST /v2/contracts carries a sample per file and no data — a prefix for text, the footer for parquet, which is where its schema lives. So agreeing costs a few megabytes whatever the files weigh, and a caller who is going to be refused finds out now rather than after four gigabytes. Every file is sampled, not just the first: one contract covers all of them, so two files that disagree are caught here.
  • There is no default schema source. schema.mode is declared (you name every column), infer (read them from the data and show me first), dataset (use the types the dataset already declares) or auto. auto is not a fourth source — it resolves to one of the three from the destination before a contract exists, and the response says which. A dataset that declares its columns has nothing to infer; one that does not exist has nothing to read.
  • An inferred schema is proposed, not agreed. It comes back state: proposed and refuses writes until PUT .../accept, so nothing is catalogued that nobody looked at. Accepting echoes the schema_fingerprint you were shown, so a proposal that moved between being read and being accepted is refused rather than confirmed blind. A declared or dataset schema arrives accepted — you already said what you meant, in the request.
  • plan[] says what happens to every column before it happens: keep, retag (relabelled, no value changes), widen (nothing is lost), cast (values are rewritten), ignored, undeclared or unsupported. values carries one real sampled value per column, which is what makes a mistyped column obvious at a glance. issues[] carries the service's own severity — blocking stops the upload, warning does not.
  • A value that will not cast is a 409 on the write that carried it, naming the column, the row and the value — not a failure at commit after everything has been sent. The file is not staged, and nothing else is affected.
  • Nothing is visible until commit. Files are written under a prefix the catalog has never named, so an upload that is abandoned, expires or fails is unreachable rather than mess somebody has to clean up. DELETE /v2/contracts/{contract_id} gives up; expires_at says how long you have.
  • Commit is idempotent on an Idempotency-Key header: a retry after a lost response returns the original snapshot instead of writing a second one.
  • A contract is checked against the catalog before every write. If the target's definition moved after the contract was agreed, the contract goes stale and is refused — rows already written were cast to a definition that no longer exists. Nothing was published, so the cost is work rather than a dataset somebody has read. Re-negotiating against the new definition is the fix.

Endpoints

ServiceDocs
Negotiate a Contractpost/v2/contracts View
Read a Contractget/v2/contracts/{contract_id} View
Amend a Proposalpatch/v2/contracts/{contract_id} View
Abandon a Contractdelete/v2/contracts/{contract_id} View
Accept a Proposalput/v2/contracts/{contract_id}/accept View
Commit a Contractpost/v2/contracts/{contract_id}/commit View
Write a Filepost/v2/contracts/{contract_id}/data View

Negotiate a Contract

Request: post/v2/contracts

Tags: contracts

Negotiate. Carries schema and samples; no upload happens here.

multipart/form-data with a contract part and one sample part per file, because a contract covers every file and two files that disagree have to be caught before anything is sent.

Header Parameters

  • authorization string | null [header; optional]

Responses

  • 201 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

post https://upload.opteryx.app/v2/contracts
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.

    

Read a Contract

Request: get/v2/contracts/{contract_id}

Tags: contracts

The whole contract: plan, issues, every write, running totals.

One document read. Nothing is listed, downloaded or re-parsed to answer it, which is what /inspect does today for every call.

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • authorization string | null [header; optional]

Responses

  • 200 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

get https://upload.opteryx.app/v2/contracts/{contract_id}
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required

    

Amend a Proposal

Request: patch/v2/contracts/{contract_id}

Tags: contracts

Retype or decline columns. Returns the whole re-planned contract.

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • authorization string | null [header; optional]

Responses

  • 200 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

patch https://upload.opteryx.app/v2/contracts/{contract_id}
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required

    

Abandon a Contract

Request: delete/v2/contracts/{contract_id}

Tags: contracts

Give up. Nothing written was ever reachable, so nothing has to be undone.

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • authorization string | null [header; optional]

Responses

  • 204 — Successful Response
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

delete https://upload.opteryx.app/v2/contracts/{contract_id}
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required

    

Accept a Proposal

Request: put/v2/contracts/{contract_id}/accept

Tags: contracts

Confirm a proposed schema, echoing the fingerprint you were shown.

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • authorization string | null [header; optional]

Responses

  • 200 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

put https://upload.opteryx.app/v2/contracts/{contract_id}/accept
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required

    

Commit a Contract

Request: post/v2/contracts/{contract_id}/commit

Tags: contracts

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • idempotency-key string | null [header; optional]
  • authorization string | null [header; optional]

Responses

  • 200 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

post https://upload.opteryx.app/v2/contracts/{contract_id}/commit
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required

    

Write a File

Request: post/v2/contracts/{contract_id}/data

Tags: contracts

Stream one file in. Answered with what it turned out to be, or refused.

Path Parameters

  • contract_id string [path; required]

Header Parameters

  • x-file-name string | null [header; optional]
  • content-type string | null [header; optional]
  • authorization string | null [header; optional]

Responses

  • 200 — Successful Response (application/json object)
  • 422 — Validation Error (application/json HTTPValidationError)

Try it live

post https://upload.opteryx.app/v2/contracts/{contract_id}/data
Bearer token required
Held in this tab only — never stored or logged. See the Authentication API for how to get one.
Path parameters
contract_idstring · required