# Craftwork Author API

For Craftwork authors: upload pack files, create packs, send them to review and read your stats.
Packs never go live without moderator review.

- OpenAPI: `https://craftwork.design/.well-known/openapi/vendor.json`
- Agent skill: `https://craftwork.design/AUTHOR-SKILL.md`
- CLI: `npx @craftwork-design/wizard login`, then `npx @craftwork-design/wizard publish ./pack.zip`

## Authentication

Create a key at `https://craftwork.design/dashboard/api` and send it as a Bearer token:

```bash
export CRAFTWORK_API_KEY=cw_live_...
curl https://craftwork.design/api/v2/vendor/me -H "Authorization: Bearer $CRAFTWORK_API_KEY"
```

Keys are shown once. Each key has scopes: `packs:read`, `packs:write`, `stats:read`. Revoke a key
in the dashboard and it stops working at once. Keys can't create or revoke other keys.

Limits:

- 120 requests a minute per key (or per OAuth session in MCP).
- Up to 3 packs a week sent to review through the API (rolling 7 days). Packs sent from the
  dashboard don't count. Past the limit save a draft (`submit: false`) and send it later.
- 10 new drafts and imports a day, 20 imports an hour.
- 60 started file uploads a day and 40 GB a week; 100 uploaded files a day across the site
  and the API. Parts bigger than the declared `size` cancel the upload.

`GET /api/v2/vendor/me` shows what is left: `uploads` and `packs`. Over a limit the API answers
`429` with `WEEKLY_PACK_LIMIT`, `DAILY_DRAFT_LIMIT`, `UPLOAD_QUOTA` or `RATE_LIMITED`.

Errors always look like this:

```json
{
  "error": { "code": "INSUFFICIENT_SCOPE", "message": "This API key needs the packs:write scope." }
}
```

Successful responses wrap the result in `data`.

## Upload a file

```http
POST /api/v2/vendor/uploads
{ "fileName": "neon-ui-kit.zip", "size": 734003200, "kind": "pack" }
```

`kind` is `pack` (the archive, up to 10 GB) or `preview` (cover and previews, up to 50 MB). The
response lists `parts`: PUT bytes `[(n-1)*partSize, n*partSize)` of the file to `parts[n-1].url`
(presigned, no auth header), then finish:

```bash
curl -X PUT --upload-file part-1 "<parts[0].url>"
curl -X POST https://craftwork.design/api/v2/vendor/uploads/<id>/complete \
  -H "Authorization: Bearer $CRAFTWORK_API_KEY"
```

The body of `complete` is optional (`{ "parts": [{ "partNumber": 1, "etag": "..." }] }`); without
it the server reads the uploaded parts itself. The response has the `fileUrl`.

## Create a pack

```http
POST /api/v2/vendor/packs
{
  "name": "Neon UI Kit",
  "shortDescription": "120 dark dashboard screens for Figma",
  "description": "What is inside, formats, counts.",
  "fileUrl": "https://marketstorage.b-cdn.net/users/.../neon-ui-kit.zip",
  "previews": ["https://marketstorage.b-cdn.net/users/.../cover.png"],
  "price": 29,
  "categories": ["figma"],
  "compatibility": ["figma"],
  "tags": ["dashboard", "dark"],
  "submit": false
}
```

Without `submit` the pack is saved as a draft you finish in the dashboard. `submit: true` sends it
to review and needs `fileUrl`, `previews`, `shortDescription`, `description` and `categories`.
`price` is the commercial license in whole US dollars (0 = free); `extendedPrice` defaults to 3x.
The first preview is the cover. Files must be uploaded through `/api/v2/vendor/uploads`.

Category slugs (up to two) and subcategory ids: `GET /api/v2/vendor/categories`.

## Import a pack

The easiest path: upload the zip, let Craftwork fill in the listing, set the price.

```http
POST /api/v2/vendor/imports
{ "fileUrl": "https://marketstorage.b-cdn.net/users/.../neon-ui-kit.zip", "sourceUrl": "https://gumroad.com/l/neon" }
```

Craftwork reads the archive (formats, asset groups, compatibility) and the pack page, copies its
images and suggests the name, short description, description, categories, tags and previews. It
takes 5-60 seconds: poll `GET /api/v2/vendor/imports/{id}` until `status` is `ready` (or
`failed`). Statuses: `queued`, `inspecting`, `fetching_link`, `generating`, `ready`, `failed`,
`applied`. Up to 20 imports an hour.

```http
POST /api/v2/vendor/imports/{id}/submit
{ "price": 24, "categories": ["figma"], "excludedGroups": ["icons/rocket"] }
```

A ready import is saved as a draft in the author's dashboard right away, without a price;
`dashboardUrl` opens it in the upload wizard and `nextStep` says what to tell the author. The
author gives the price in chat or sets it on the site. Everything you leave out comes from
`suggestion`. `price` is required to send to review; `submit: false` only updates the draft.
After a review submission the asset groups inside the zip are added to the pack in the
background (`assets` shows the progress). A second submit answers `409 ALREADY_SUBMITTED`; if
the author already sent or deleted the draft on the site, submit answers `409 DRAFT_GONE`.

## Deleting a pack

Authors can't delete packs directly. Ask moderators instead:

```http
POST /api/v2/vendor/packs/{id}/deletion-request
{ "reason": "Replaced by version 2" }
```

The pack stays live until a moderator approves; then it leaves the catalog and sale, and people
who bought it keep their downloads. The author gets the answer by email. `DELETE` on the same
path withdraws an open request. A second request for the same pack answers `409 ALREADY_REQUESTED`.

## Your packs and stats

```http
GET /api/v2/vendor/packs?status=review&limit=50
GET /api/v2/vendor/packs/{id}
GET /api/v2/vendor/stats?range=30d
```

Statuses: `review`, `published`, `changes_required`, `declined`, `draft`.

## MCP

The same key works with the Craftwork MCP server (`https://craftwork.design/api/mcp`, header
`Authorization: Bearer cw_live_...`). With a key only the author tools are available:
`craftwork_vendor_list_packs`, `craftwork_vendor_start_upload`, `craftwork_vendor_complete_upload`,
`craftwork_vendor_create_import`, `craftwork_vendor_import_status`, `craftwork_vendor_submit_import`,
`craftwork_vendor_create_pack`.
