Basalt docs
basaltapp.io

Import

Bringing a Notion export, a Confluence space or a folder of Markdown into a collection, and the read-only connections that keep doing it.

11 operations. Every path is relative to the instance origin; every response body is JSON unless stated. The endpoint descriptions below come straight from the server’s own route table. See API for authentication, errors and pagination.

POST /api/v1/workspaces/{workspaceId}/import

Import a Notion zip, a Markdown/Obsidian folder or a Basalt export into a collection (multipart/form-data: one or more file parts — a .zip is expanded, loose files keep their relative path — plus either an existing collectionId (requires edit on it) or a collectionName to create one (requires collections.create). Answers a report of what came in and what did not: unresolved links, skipped files, inferred column types and the decisions taken on your behalf. Not transactional and not rolled back on partial failure — every id is derived from the source path, so re-running the same archive into the same collection resumes it instead of duplicating it.

importWorkspace · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required

Response 200 — application/json

  • formatVersion · 2 — required
  • runId · string (uuid) — required
  • status · "complete" | "partial" — required
  • source · "basalt" | "notion" | "markdown" | "confluence" — required
  • collectionId · string (uuid) — required
  • collectionName · string — required
  • collectionCreated · boolean — required
  • counts · object — required
    • pages · integer — required, -9007199254740991–9007199254740991
    • databases · integer — required, -9007199254740991–9007199254740991
    • rows · integer — required, -9007199254740991–9007199254740991
    • fieldsCreated · integer — required, -9007199254740991–9007199254740991
    • fieldsReused · integer — required, -9007199254740991–9007199254740991
    • attachments · integer — required, -9007199254740991–9007199254740991
    • linksRewritten · integer — required, -9007199254740991–9007199254740991
    • linksUnresolved · integer — required, -9007199254740991–9007199254740991
  • unresolved · object[] — required
    • from · string — required
    • target · string — required
  • skipped · object[] — required
    • path · string — required
    • reason · "unsupported-file" | "unsupported-image" | "html-export" | "too-large" | "empty" | "duplicate-view-csv" — required
  • failed · object[] — required
    • path · string — required
    • message · string — required
  • notes · object[] — required
    • code · "people-as-text" | "relations-as-text" | "formulas-as-text" | "ambiguous-dates-as-text" | "numbers-as-text" | "fields-not-created" | "field-key-collision" | "builtin-options-frozen" | "timestamps-not-preserved" | "authors-not-preserved" | "views-not-imported" | "comments-not-imported" | "row-body-unmatched" | "links-unresolved" | "storage-limit-reached" | "source-urls-not-rewritten" | "notion-blocks-unsupported" | "notion-formatting-lost" | "notion-files-not-transferred" | "confluence-macros-unsupported" | "confluence-attachments-skipped" | "confluence-attachments-quota" | "confluence-mentions-not-imported" | "source-objects-vanished" | "source-objects-unreachable" — required
    • count · integer — required, -9007199254740991–9007199254740991
    • samples · string[] — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

GET /api/v1/workspaces/{workspaceId}/import/runs

The history of imports into this workspace, newest first. One entry per run, whatever the run was fed by. Runs whose target collection the caller may not read are omitted rather than refused — the history is a view of the collections you can see, not a second way to enumerate them.

listImportRuns · token scope: content:read

Path parameters

  • workspaceId · string (uuid) — required

Query parameters

  • cursor · string — length 1–∞
  • limit · integer — 1–100

Response 200 — application/json

  • items · object[] — required
    • id · string (uuid) — required
    • source · "basalt" | "notion" | "markdown" | "confluence" — required
    • status · "complete" | "partial" — required
    • collectionId · string (uuid) — required
    • collectionName · string — required
    • startedBy · object | null — required
      • id · string (uuid) — required
      • name · string | null — required
    • startedAt · string (date-time) — required
    • finishedAt · string (date-time) — required
    • counts · object — required
      • pages · integer — required, -9007199254740991–9007199254740991
      • databases · integer — required, -9007199254740991–9007199254740991
      • rows · integer — required, -9007199254740991–9007199254740991
      • fieldsCreated · integer — required, -9007199254740991–9007199254740991
      • fieldsReused · integer — required, -9007199254740991–9007199254740991
      • attachments · integer — required, -9007199254740991–9007199254740991
      • linksRewritten · integer — required, -9007199254740991–9007199254740991
      • linksUnresolved · integer — required, -9007199254740991–9007199254740991
    • linkCount · integer — required, -9007199254740991–9007199254740991
    • linksTruncated · boolean — required
  • nextCursor · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

GET /api/v1/workspaces/{workspaceId}/import/runs/{runId}

One import run with its full report: counts, the decisions taken on your behalf, skipped files, failures and a bounded sample of unresolved links. Requires read on the collection it imported into. The link map is a separate, paginated resource — see /links.

getImportRun · token scope: content:read

Path parameters

  • workspaceId · string (uuid) — required
  • runId · string (uuid) — required

Response 200 — application/json

  • id · string (uuid) — required
  • source · "basalt" | "notion" | "markdown" | "confluence" — required
  • status · "complete" | "partial" — required
  • collectionId · string (uuid) — required
  • collectionName · string — required
  • startedBy · object | null — required
    • id · string (uuid) — required
    • name · string | null — required
  • startedAt · string (date-time) — required
  • finishedAt · string (date-time) — required
  • counts · object — required
    • pages · integer — required, -9007199254740991–9007199254740991
    • databases · integer — required, -9007199254740991–9007199254740991
    • rows · integer — required, -9007199254740991–9007199254740991
    • fieldsCreated · integer — required, -9007199254740991–9007199254740991
    • fieldsReused · integer — required, -9007199254740991–9007199254740991
    • attachments · integer — required, -9007199254740991–9007199254740991
    • linksRewritten · integer — required, -9007199254740991–9007199254740991
    • linksUnresolved · integer — required, -9007199254740991–9007199254740991
  • linkCount · integer — required, -9007199254740991–9007199254740991
  • linksTruncated · boolean — required
  • report · object — required
    • formatVersion · 2 — required
    • runId · string (uuid) — required
    • status · "complete" | "partial" — required
    • source · "basalt" | "notion" | "markdown" | "confluence" — required
    • collectionId · string (uuid) — required
    • collectionName · string — required
    • collectionCreated · boolean — required
    • counts · object — required
      • pages · integer — required, -9007199254740991–9007199254740991
      • databases · integer — required, -9007199254740991–9007199254740991
      • rows · integer — required, -9007199254740991–9007199254740991
      • fieldsCreated · integer — required, -9007199254740991–9007199254740991
      • fieldsReused · integer — required, -9007199254740991–9007199254740991
      • attachments · integer — required, -9007199254740991–9007199254740991
      • linksRewritten · integer — required, -9007199254740991–9007199254740991
      • linksUnresolved · integer — required, -9007199254740991–9007199254740991
    • unresolved · object[] — required
      • from · string — required
      • target · string — required
    • skipped · object[] — required
      • path · string — required
      • reason · "unsupported-file" | "unsupported-image" | "html-export" | "too-large" | "empty" | "duplicate-view-csv" — required
    • failed · object[] — required
      • path · string — required
      • message · string — required
    • notes · object[] — required
      • code · "people-as-text" | "relations-as-text" | "formulas-as-text" | "ambiguous-dates-as-text" | "numbers-as-text" | "fields-not-created" | "field-key-collision" | "builtin-options-frozen" | "timestamps-not-preserved" | "authors-not-preserved" | "views-not-imported" | "comments-not-imported" | "row-body-unmatched" | "links-unresolved" | "storage-limit-reached" | "source-urls-not-rewritten" | "notion-blocks-unsupported" | "notion-formatting-lost" | "notion-files-not-transferred" | "confluence-macros-unsupported" | "confluence-attachments-skipped" | "confluence-attachments-quota" | "confluence-mentions-not-imported" | "source-objects-vanished" | "source-objects-unreachable" — required
      • count · integer — required, -9007199254740991–9007199254740991
      • samples · string[] — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

The link map of one run: old link → new id, one row per way the thing used to be addressed (an archive path, a notion.so URL, an Obsidian [[wikilink]], a source-workspace id). This is what you point at the systems that still link to the old location. Filter with q (substring of the old link or the title) and sourceKind; paginate with cursor.

listImportRunLinks · token scope: content:read

Path parameters

  • workspaceId · string (uuid) — required
  • runId · string (uuid) — required

Query parameters

  • cursor · string — length 1–∞
  • limit · integer — 1–100
  • q · string — length 1–200
  • sourceKind · "archive-path" | "notion-url" | "wikilink" | "basalt-id" | "remote-url"

Response 200 — application/json

  • items · object[] — required
    • sourceKind · "archive-path" | "notion-url" | "wikilink" | "basalt-id" | "remote-url" — required
    • source · string — required
    • targetKind · "page" | "database" — required
    • targetId · string (uuid) — required
    • title · string — required
  • nextCursor · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

GET /api/v1/workspaces/{workspaceId}/import-connections

The workspace’s saved import connections. Credentials are never included — only a hint.

listImportConnections · token scope: content:read

Path parameters

  • workspaceId · string (uuid) — required

Response 200 — application/json

  • items · object[] — required
    • id · string (uuid) — required
    • workspaceId · string (uuid) — required
    • collectionId · string (uuid) — required
    • name · string — required
    • provider · "confluence" | "notion" — required
    • baseUrl · string — required
    • account · string — required
    • credentialHint · string — required
    • spaces · string[] — required
    • targetPageId · string (uuid) | null — required
    • spaceMap · object[] — required
      • space · string — required, length 1–64
      • collectionId · string (uuid) — required
    • createdAt · string (date-time) — required
    • lastRunAt · string (date-time) | null — required
    • lastRunId · string (uuid) | null — required
    • job · object | null — required
      • id · string (uuid) — required
      • status · "queued" | "running" | "complete" | "partial" | "failed" | "cancelled" — required
      • phase · "fetching" | "writing" | null — required
      • pagesFetched · integer — required, -9007199254740991–9007199254740991
      • pagesWritten · integer — required, -9007199254740991–9007199254740991
      • attempt · integer — required, -9007199254740991–9007199254740991
      • runId · string (uuid) — required
      • queuedAt · string (date-time) — required
      • startedAt · string (date-time) | null — required
      • finishedAt · string (date-time) | null — required
      • error · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

POST /api/v1/workspaces/{workspaceId}/import-connections

Save a connection to a remote system (Confluence or Notion). Both are read-only: nothing Basalt does can change the connected system. baseUrl and account are required for Confluence and ignored for Notion, which has one address and a bearer token. The credential is stored encrypted and is never returned by any route.

createImportConnection · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required

Request body — application/json, required

  • name · string — length 0–120
  • provider · "confluence" | "notion" — required
  • baseUrl · string (uri)
  • account · string — length 1–320
  • credential · string — required, length 1–2048
  • collectionId · string (uuid) — required
  • targetPageId · string (uuid)
  • spaceMap · object[] — 0–100 items
    • space · string — required, length 1–64
    • collectionId · string (uuid) — required
  • spaces · string[] — 0–100 items

Response 200 — application/json

  • id · string (uuid) — required
  • workspaceId · string (uuid) — required
  • collectionId · string (uuid) — required
  • name · string — required
  • provider · "confluence" | "notion" — required
  • baseUrl · string — required
  • account · string — required
  • credentialHint · string — required
  • spaces · string[] — required
  • targetPageId · string (uuid) | null — required
  • spaceMap · object[] — required
    • space · string — required, length 1–64
    • collectionId · string (uuid) — required
  • createdAt · string (date-time) — required
  • lastRunAt · string (date-time) | null — required
  • lastRunId · string (uuid) | null — required
  • job · object | null — required
    • id · string (uuid) — required
    • status · "queued" | "running" | "complete" | "partial" | "failed" | "cancelled" — required
    • phase · "fetching" | "writing" | null — required
    • pagesFetched · integer — required, -9007199254740991–9007199254740991
    • pagesWritten · integer — required, -9007199254740991–9007199254740991
    • attempt · integer — required, -9007199254740991–9007199254740991
    • runId · string (uuid) — required
    • queuedAt · string (date-time) — required
    • startedAt · string (date-time) | null — required
    • finishedAt · string (date-time) | null — required
    • error · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

DELETE /api/v1/workspaces/{workspaceId}/import-connections/{connectionId}

Delete a connection. Imported pages and past run reports are kept.

deleteImportConnection · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required
  • connectionId · string (uuid) — required

Response 200 — application/json

  • ok · true — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

POST /api/v1/workspaces/{workspaceId}/import-connections/{connectionId}/probe

Check the credential and list what it can read — Confluence spaces, or the Notion pages and databases the integration has been shared with — without importing anything. A POST because it makes an outbound request on the caller’s behalf.

probeImportConnection · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required
  • connectionId · string (uuid) — required

Response 200 — application/json

  • ok · boolean — required
  • spaces · object[] — required
    • key · string — required
    • name · string — required
  • message · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

POST /api/v1/workspaces/{workspaceId}/import-connections/test

Check credentials that have not been saved yet, and list what they can read. Same answer as probeImportConnection, for a connection that does not exist: this is how a form finds out a token is wrong BEFORE creating a connection nobody can edit afterwards. Nothing is stored — the credential is used for one outbound request.

testImportCredentials · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required

Request body — application/json, required

  • provider · "confluence" | "notion" — required
  • baseUrl · string (uri)
  • account · string — length 1–320
  • credential · string — required, length 1–2048

Response 200 — application/json

  • ok · boolean — required
  • spaces · object[] — required
    • key · string — required
    • name · string — required
  • message · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

POST /api/v1/workspaces/{workspaceId}/import-connections/{connectionId}/run

Queue an import from the connection, reading only, and answer immediately. The work happens in a background job — walking a production wiki takes minutes to hours, and it used to happen inside this request. Poll the connection list for the job’s status; when it finishes, runId names the run report it wrote. Convergent: ids are derived from the source path, so a re-run updates the pages it created before instead of duplicating them — but a page RENAMED or moved in the source system is a new path and therefore a new page, which the run’s link map shows. Asking twice while a job is already queued or running answers that job rather than starting a second one.

runImportConnection · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required
  • connectionId · string (uuid) — required

Response 200 — application/json

  • id · string (uuid) — required
  • status · "queued" | "running" | "complete" | "partial" | "failed" | "cancelled" — required
  • phase · "fetching" | "writing" | null — required
  • pagesFetched · integer — required, -9007199254740991–9007199254740991
  • pagesWritten · integer — required, -9007199254740991–9007199254740991
  • attempt · integer — required, -9007199254740991–9007199254740991
  • runId · string (uuid) — required
  • queuedAt · string (date-time) — required
  • startedAt · string (date-time) | null — required
  • finishedAt · string (date-time) | null — required
  • error · string | null — required

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64

POST /api/v1/workspaces/{workspaceId}/import-connections/{connectionId}/purge

Move everything this connection imported into the trash, so an import can be redone from clean. Only blocks the importer itself created are touched — a hand-made page is never trashed, and a hand-made page that lived under an imported one is moved to the collection root instead of going down with it. Nothing is deleted for good here: emptyTrash is still the only route that removes data, and everything remains restorable until it runs.

purgeImportConnection · token scope: content:write

Path parameters

  • workspaceId · string (uuid) — required
  • connectionId · string (uuid) — required

Response 200 — application/json

  • trashed · integer — required, -9007199254740991–9007199254740991
  • reparented · integer — required, -9007199254740991–9007199254740991
  • skipped · integer — required, -9007199254740991–9007199254740991

Response default — application/json

  • error · object — required
    • code · "bad_request" | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "conflict" | "rate_limited" | "internal" — required
    • message · string — required
    • reason · string — length 1–64