REST API quickstart

Make your first call; link out to the live OpenAPI spec.

This page gets you from zero to a successful call against the public Dalea API — the same contract the official SDKs are generated from. For the complete operation catalogue, see the live OpenAPI reference at dalea.app/api-docs: every endpoint, schema, and example, including a ready-to-copy SDK snippet (TypeScript and Python) on each operation.

Prefer an SDK?

The official Dalea SDK (@dalea/sdk / dalea) wraps everything on this page — auth headers, workspace scoping, retries, pagination, typed errors — so you write dalea.documents.list() instead of raw HTTP.

Base URL

Cloud: https://dalea.app. Enterprise dedicated tenants: substitute your tenant URL.

All public endpoints live under /api/v1/. Stable; backwards-compatible within the major version.

Authentication

Public calls authenticate with a workspace API key sent on the X-API-Key header. The fastest path:

  1. Get a workspace API key

    Settings → Developer → API Keys → Create API Key. Pick a workspace and a scope (Self, a role you already hold, or Custom API Scope).

  2. Send it on the X-API-Key header
    X-API-Key: dalea_xxxxxxxxx

An API key is bound to one workspace when it's created — the workspace is embedded in the key, so you don't send any workspace header alongside it.

Third-party apps acting on behalf of a user use OAuth 2.1 instead, sending the access token as Authorization: Bearer <token> (also workspace-bound at authorization time). See Authentication for the complete picture, including OAuth and MCP tokens.

Your first call

List the documents you can see:

curl -sS "https://dalea.app/api/v1/documents?limit=10" \
  -H "X-API-Key: $DALEA_API_KEY"

Response shape

List responses are keyed by the resource name, with pagination fields alongside where the endpoint supports them:

// GET /api/v1/documents
{
  "documents": [ /* … */ ],
  "nextCursor": "cmd2..."   // present when more pages exist
}

// GET /api/v1/tables/{tableId}/objects
{
  "objects": [ /* … */ ],
  "total": 4213,
  "limit": 50,
  "offset": 0
}

Single-resource reads are wrapped the same way, in a single-key envelope named after the resource: GET /api/v1/documents/{id} returns { "document": … }, GET /api/v1/tables/{id} returns { "table": … }, GET /api/v1/results/batches/{id} returns { "batch": … }. A few operations return the object bare, notably GET /api/v1/files/{id} and the entity-version reads; check the reference per operation.

Pagination

Pagination is per-endpoint. The OpenAPI reference documents each endpoint's exact parameters:

  • Cursor-paged lists (documents, files) return a nextCursor; pass it back as the cursor query param to get the next page. There is no fallback offset mode on these endpoints.
  • Offset-paged lists (data objects and friends) take limit + offset and return a total.

For very large pulls, set limit to its documented maximum to reduce round-trips, or use the SDKs' auto-paging helpers on the surfaces that have one.

Filtering

Many list endpoints accept filter query params — camelCase, matching the spec:

GET /api/v1/documents?projectId=proj_123&archived=false
GET /api/v1/results/batches?finalized=true&schemaId=sch_456

Allowed filter fields are documented per-endpoint in the OpenAPI reference. Apply them at the API layer rather than fetching everything and filtering client-side — it's faster and respects rate limits.

Errors

Standard HTTP semantics:

StatusWhat it meansTypical fix
400 / 422Bad request — malformed or invalid inputRead the message; fix the payload.
401Missing or invalid credentialsCheck the X-API-Key header; rotate the key if revoked.
403Authenticated but not allowedYour role doesn't grant this action — or the endpoint is first-party-only (ENDPOINT_NOT_PUBLIC).
404Not found, or you don't have permission to know it existsCheck the ID, and that the resource lives in the key's workspace.
409Conflict — usually a uniqueness violationRead the code; retry with different input or merge state.
429Rate limit exceededBack off (exponential, with jitter); honor Retry-After.
5xxDalea side errorRetry with backoff; persistent 5xx is worth filing a support ticket.

Error responses carry a flat body with a machine-readable code:

{
  "code": "VALIDATION_ERROR",
  "error": "Bad Request",
  "message": "title is required"
}

Reacting to changes

Native event webhooks are on the roadmap but not yet available. Until they ship, the supported pattern is polling a list endpoint on a schedule, using its filters to narrow the pull:

# Every 5 minutes, fetch finalized result batches and diff against what you've seen
resp = requests.get(
    "https://dalea.app/api/v1/results/batches",
    headers={"X-API-Key": api_key},
    params={"finalized": "true"},
)
for batch in resp.json()["batches"]:
    if batch["id"] not in seen:
        handle_finalized_batch(batch)

A batch's status is active or superseded: a correction is recorded as a new batch that supersedes the old one, so poll on finalized rather than looking for a close event.

Five minutes is a reasonable default for most workflows; tighten if you have truly time-sensitive needs. Persist what you've already processed durably so a restart doesn't replay history.

What you can do

The public surface spans five domains — the same five the Dalea SDK exposes. The OpenAPI reference at dalea.app/api-docs is the canonical list of operations with stable, published paths:

  • Data — environments, tables, columns, objects, the query engine, saved queries, result batches, schema validation, naming schemes, import mappings, and the reversible archive lifecycle.
  • Documents — document metadata plus the markdown round-trip: read a document as markdown, append/insert/update/replace blocks.
  • Inventory — item types (read-only; they are data tables designed in the environment designer), lots, containers, placements (check-in/out, move), a read-only item query, consumption, and audit trails.
  • Search — unified search across documents, files, data objects, and result schemas, with type/scope filters and offset pagination.
  • Storage — file uploads (multipart, base64), metadata, presigned downloads, and storage-usage breakdowns.

Everything else — the AI suite, org/workspace settings, entity deletion, and first-party surfaces like notifications — is not part of the public API and returns 403 ENDPOINT_NOT_PUBLIC for API-key and OAuth callers.

Idiomatic patterns

The exact paths, request shapes, and response schemas live in the OpenAPI reference. Always check there before writing code.

Listing entities is an HTTP GET with optional filter and pagination params, scoped to the API key's workspace.

Creating entities is POST with a JSON body; updates are PATCH. Writes return the affected resource.

Bulk operations exist for high-volume data surfaces (creating many objects, columns, or containers at once). They follow the same pattern but accept arrays — look for /bulk paths in the spec.

Lifecycle is reversible: POST …/{id}/archive and POST …/{id}/restore instead of DELETE. The public surface has exactly one DELETE, DELETE /api/v1/documents/{id}/markdown/blocks, which removes blocks from a document body and requires an audit reason.

The spec is the source of truth

Every operation in the public REST API is documented at dalea.app/api-docs with full request and response schemas, plus an SDK snippet you can copy instead of hand-rolling the HTTP call. If something looks ambiguous, check there before guessing: the spec is generated from the same handlers your call hits.

What's next