craaft Imports API

The Imports API from craaft — 2 operation(s) for imports.

GitHub OpenAPI

Operations 2

POST /imports/preview Preview a board import #
POST /imports Import a board #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/craaft-io:craaft-io-imports-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

craaft-io-imports-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Craaft Imports API
  version: 1.0.0
  description: 'Personal-access-token API for Craaft. See `docs/api.md` for the

    narrative guide, rate limits, and worked examples.'
  contact:
    name: Craaft
servers:
- url: https://craaft.io/api/v1
  description: Production
- url: http://localhost:8080/api/v1
  description: Local
security:
- bearerAuth: []
tags:
- name: Imports
paths:
  /imports/preview:
    post:
      summary: Preview a board import
      description: 'Parses an uploaded Trello, Jira, Asana, CSV or craaft export and

        returns what `POST /imports` would create. Nothing is written. The

        preview and the import run the same code, so the counts match.


        `multipart/form-data`, file up to 5 MB. Limits per import: 2,000

        cards, 200 columns, 500 checklist items on any one card, 20,000

        checklist items and 20,000 comments in all, 200 CSV headers.

        `source` is detected from the file when omitted. For CSV sources,

        `mapping` replaces the server''s suggested mapping: send every field

        you want imported. The response then carries a `csv` block with the

        headers, the mapping in use and the first sample rows. When no title

        column can be guessed and no `mapping` is sent, the preview still

        answers `200` with the `csv` block and an empty summary named after

        the file, so the client can map the title and preview again.


        Matched assignees who aren''t you or a workspace owner or admin are

        listed in `access`: the import adds them to the new private board

        as contributors.'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ImportRequest'
      responses:
        '200':
          description: What the import would create
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportPreview'
        '400':
          description: 'No file, an unknown `source`, a malformed `mapping`, a file that

            isn''t a recognised export of the given source, a CSV with no

            `title` in the `mapping` sent, a CSV with more than 200 headers

            ("This file has too many columns to map. The limit is 200"), or

            a file that isn''t UTF-8 ("Save the file as UTF-8 CSV and try

            again").

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '413':
          description: The file is over 5 MB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: 'Over a limit, worded with the real numbers: more than 2,000

            cards ("This file has 2,001 cards. The limit is 2,000"; a CSV

            stops counting at the limit, "This file has more than 2,000

            cards. The limit is 2,000"), more than 200 columns ("This file

            has 238 columns. The limit is 200"), more than 500 checklist

            items on a card ("A card has 1,234 checklist items. The limit is

            500 per card"), more than 20,000 checklist items or comments in

            all ("This file has 25,000 comments. The limit is 20,000"); or

            no cards and no columns to import.

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/ImportBusy'
      tags:
      - Imports
      operationId: postImportsPreview
      x-operation-id-source: derived
  /imports:
    post:
      summary: Import a board
      description: 'Creates a new board from an uploaded Trello, Jira, Asana, CSV or

        craaft export. It never writes into an existing board. Takes the same

        fields as `POST /imports/preview` and runs the same parse. All or

        nothing: a failed import leaves no board behind. The board is

        private; each matched assignee listed in the preview''s `access` is

        added to it as a contributor. A craaft export''s board description

        comes across. No notifications

        are sent (no emails, mention, assignment or follower notifications,

        no Slack or Discord messages). The new board counts against the

        workspace''s board cap.


        If the board is created but reloading it fails, the `201` still

        carries at least `id` and `name`.'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ImportRequest'
      responses:
        '201':
          description: Created. Shaped like the `POST /projects` response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project'
        '400':
          description: 'Same cases as `POST /imports/preview`, and a CSV with no `title`

            mapping, sent or guessed ("Map a column to the card title")

            '
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Plan limit reached (board cap). Same body as `POST /projects`.
          content:
            application/json:
              schema:
                type: object
                required:
                - error
                - limit
                - currentPlan
                - max
                - current
                properties:
                  error:
                    type: string
                    example: project limit reached
                  limit:
                    type: string
                    example: projects
                  currentPlan:
                    type: string
                  max:
                    type: integer
                  current:
                    type: integer
        '413':
          description: The file is over 5 MB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Over a limit (same cases as `POST /imports/preview`), or no cards and no columns to import
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '503':
          $ref: '#/components/responses/ImportBusy'
      tags:
      - Imports
      operationId: postImports
      x-operation-id-source: derived
components:
  responses:
    Unauthorized:
      description: Missing or invalid token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ImportBusy:
      description: 'Too many imports are running and none finished within 15 seconds

        ("Too many imports are running. Try again in a moment"). Retry after

        the `Retry-After` delay.

        '
      headers:
        Retry-After:
          schema:
            type: integer
            example: 5
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    ImportPreview:
      type: object
      required:
      - source
      - name
      - columns
      - totals
      - assignees
      - access
      - warnings
      properties:
        source:
          type: string
          enum:
          - trello
          - csv
          - jira
          - asana
          - craaft
        name:
          type: string
          maxLength: 200
        columns:
          type: array
          items:
            type: object
            required:
            - title
            - isDone
            - cards
            properties:
              title:
                type: string
                maxLength: 80
              isDone:
                type: boolean
              cards:
                type: integer
        totals:
          type: object
          required:
          - cards
          - archived
          - comments
          - checklistItems
          - tags
          properties:
            cards:
              type: integer
            archived:
              type: integer
            comments:
              type: integer
            checklistItems:
              type: integer
            tags:
              type: integer
        assignees:
          type: object
          required:
          - matched
          - unmatched
          properties:
            matched:
              type: array
              items:
                type: object
                required:
                - source
                - userId
                - name
                properties:
                  source:
                    type: string
                  userId:
                    type: string
                  name:
                    type: string
            unmatched:
              type: array
              items:
                type: string
        access:
          type: array
          description: 'Matched assignees the import adds to the new private board as

            contributors: everyone matched except you and the workspace

            owners and admins, who already see every board.

            '
          items:
            type: object
            required:
            - userId
            - name
            properties:
              userId:
                type: string
              name:
                type: string
        warnings:
          type: array
          items:
            type: object
            required:
            - code
            - message
            properties:
              code:
                type: string
                example: closed_lists_skipped
              message:
                type: string
        csv:
          type: object
          description: Present only for CSV sources (generic, Jira and Asana)
          properties:
            headers:
              type: array
              items:
                type: string
            mapping:
              type: object
              description: The mapping in use, as sent or as suggested
              additionalProperties:
                type: string
            sample:
              type: array
              description: The first 3 data rows
              items:
                type: array
                items:
                  type: string
    Project:
      type: object
      required:
      - id
      - workspaceId
      - name
      - createdAt
      - updatedAt
      properties:
        id:
          type: string
          format: uuid
        workspaceId:
          type: string
          format: uuid
        name:
          type: string
          maxLength: 200
        description:
          type: string
        brief:
          type: string
          maxLength: 20000
          description: 'The board brief (Markdown): goals, constraints, voice, decisions and links for anyone working on the board. On GET and PATCH /projects/{id} only; the list leaves it out. Every card''s prompt ends with it.'
        isFavorite:
          type: boolean
        publicToken:
          type: string
          description: Non-empty when public sharing is on
        backgroundImage:
          type: string
        backgroundColor:
          type: string
          description: Board background hex color (#rrggbb), empty when unset; mutually exclusive with backgroundImage
        colorScheme:
          type: string
        textColor:
          type: string
          enum:
          - dark
          - light
        showMilestones:
          type: boolean
          description: Whether the board's top bar shows the milestones button. Off on new boards (since 28 October 2026); boards made before then keep it on unless changed.
        showArchived:
          type: boolean
          description: Whether the board's top bar shows the archived cards button. Off on new boards (since 28 October 2026); boards made before then keep it on unless changed.
        showFiles:
          type: boolean
          description: Whether the board's top bar shows the files button. Off on new boards (since 28 October 2026); boards made before then keep it on unless changed.
        showStandup:
          type: boolean
          description: Whether the board's top bar shows the standup button. Off on new boards (since 28 October 2026); boards made before then keep it on unless changed.
        showActivity:
          type: boolean
          description: Whether the board's top bar shows the activity button. Off by default. The /projects/{id}/activity feed answers either way.
        myRole:
          type: string
          enum:
          - owner
          - admin
          - member
          description: Caller's role in the project's *workspace* (distinct from myBoardRole).
        visibility:
          type: string
          enum:
          - private
          - workspace
          description: '`private` (default): only users with an explicit grant (plus

            workspace owners/admins) see the board. `workspace`: every

            workspace member is an implicit contributor.

            '
        myBoardRole:
          type: string
          enum:
          - admin
          - contributor
          description: 'Caller''s effective role on the board. Workspace owners and admins

            are always `admin`; explicit grants resolve to `admin` or

            `contributor`; on a `workspace`-visibility board, plain workspace

            members default to `contributor`.

            '
        canUploadAttachments:
          type: boolean
          description: 'Whether cards on this board accept file attachments. Reflects the

            board''s *workspace* plan (Pro), NOT the caller''s own plan - so a

            Free user collaborating on a Pro workspace''s board sees `true`.

            Matches the server''s upload gate; the SPA uses it to show/hide the

            attachment dropzone.

            '
        totalCards:
          type: integer
        columnCounts:
          type: object
          additionalProperties:
            type: integer
        workspaceName:
          type: string
          description: Workspace display name; populated on list responses.
        members:
          type: array
          description: Capped avatar-stack preview on list responses only.
          items:
            $ref: '#/components/schemas/ProjectMemberPreview'
        columns:
          type: array
          items:
            $ref: '#/components/schemas/Column'
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    Column:
      type: object
      required:
      - id
      - key
      - title
      - position
      properties:
        id:
          type: string
          format: uuid
        key:
          type: string
          description: Stable identifier for cards.column
        title:
          type: string
          maxLength: 80
        color:
          type: string
          description: Optional palette token; empty for default
        position:
          type: number
        isDone:
          type: boolean
        cardLimit:
          type: integer
          nullable: true
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
    ProjectMemberPreview:
      type: object
      required:
      - id
      - name
      - avatarUrl
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        avatarUrl:
          type: string
          description: Empty when no upload; otherwise /api/v1/users/{id}/avatar
    ImportRequest:
      type: object
      required:
      - file
      properties:
        file:
          type: string
          format: binary
          description: The export file, up to 5 MB
        source:
          type: string
          enum:
          - trello
          - csv
          - jira
          - asana
          - craaft
          description: Detected from the file when omitted
        mapping:
          type: string
          description: 'CSV sources only. A JSON object mapping craaft fields to CSV

            header names, for example `{"title":"Summary","column":"Status"}`.

            Fields: `title` (required), `description`, `column`, `dueDate`,

            `priority`, `tags`, `assignee`, `archived`. It replaces the

            server''s suggested mapping, so send every field to import.

            '
        name:
          type: string
          description: Board name. Defaults to the name in the file.
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: cookie
      name: craaft_session
      description: 'Browser session cookie (`__Host-craaft_session` in production,

        `craaft_session` in dev), set at sign-in. Not obtainable through

        this API - it''s here only so the handful of session-only routes

        below (the OAuth consent screen''s own endpoints, connected-apps

        management) can be marked accurately. They reject both bearer

        schemes with a 403.

        '
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: cra_<24 base64url chars>
      description: 'Two token schemes, same permissions. `cra_<24 base64url chars>` is a

        personal access token minted in Settings, doesn''t expire on its own,

        and is revoked from the same page. `cro_<32 base64url chars>` is an

        OAuth access token issued to an OAuth client (an MCP client such as

        Claude) via the authorization-code-with-PKCE flow; it expires after

        1 hour and the client refreshes it. Either token authenticates as

        the user who authorized it, with that user''s exact permissions.


        A token can be read-only: a personal access token created with

        access `read`, or an OAuth grant with scope `craaft:read`. A

        read-only token can send GET, HEAD and OPTIONS; any other method

        gets `403` with "this token is read-only; this request needs a

        token with read and write access", before the request is handled.


        A token can also be limited to chosen boards. Other boards then

        behave as if they don''t exist (`404`, or left out of lists), and

        `POST /projects`, `/imports`, `/members` and `/invitations` answer

        `403` with "this token is limited to some boards; this request

        needs a token for all boards".


        OAuth clients discover every endpoint they need - authorization,

        token, dynamic client registration, revocation - at

        `/.well-known/oauth-authorization-server` (RFC 8414); they don''t

        need to be told the URLs out of band.

        '