| What you want to do | Actual call |
|---|---|
| Leave a note/comment/post a completion summary on an issue | PUT /api/issues/:iid/collab/summary(posting a comment with an API Key always returns 40301) |
| Mark as done | PUT /api/issues/:iid/internal-meta set workflow_status=done(not state) |
| Close issue | Not possible—JWT only; passing an API Key to state always returns 40301 |
| Started working on it | PUT /api/issues/:iid/internal-meta set in_progress |
| Record consensus/decisions | PUT /api/issues/:iid/collab/consensus |
By INT-xxx find issue |
GET /api/projects/:pid/issues?q=INT-56 switch to UUID (path parameters only accept UUIDs) |
| Attach a PR to an issue | POST /api/issues/:iid/pull-requests,body {"url":"<PR链接>"} |
| Refresh PR status | POST /api/issues/:iid/pull-requests/sync(call after merging; for per-line failures see failures[]) |
| Remove a PR | DELETE /api/issues/:iid/pull-requests/:lid(:lid is the link row id, i.e. the {id}) |
Before calling any write endpoint, first read references/api.md the corresponding section; do not construct the request body from memory.
In the table above, :pid in api.md is {id},:iid is {iid};:lid is the PR link row id (in api.md, pull-requests under the path, {id})。
Authentication configuration
Fast Ship uses API Key for programmatic authentication. The key format is a random string starting with fsk_ .
Request method (hard rules)
- ProhibitedReading the API Key out and writing it into commands, parameters, environment variable assignments, or any output—
curl -H "Authorization: Bearer fsk_..."such usage leaves the Key in session logs and process lists. - All requests must go through the skill's bundled script. The script reads config itself and attaches the authentication header, so the Key never appears on the command line:
node <技能目录>/scripts/fast-ship-api.mjs <METHOD> <path> [body.json] [--verify [读回路径]]
- When a request body is required, write the JSON to a UTF-8 file and pass it as the
body.jsonpositional parameter. --verify [读回路径]: after a successful write request (2xx), send one more GET and output{ "response": …, "verify": … }, for read-back verification after write. By default it GETs the same path; for write endpoints without a corresponding GET route (e.g.internal-meta、recommendation、collabPUT; for batchPUT /api/issues/internal-metathe default read-back will hitGET /issues/:iidand 404) you must explicitly provide the read-back path (e.g. for the Issue itself,GET /api/issues/:iid). For DELETE, a read-back 404 is treated as confirmation of deletion.- Fallback when the script is unavailable: write the
Authorization: Bearer <api_key>line as a whole into a temporary header file, reference it withcurl -H @<header文件>, and after usedelete immediatelythat file.
First use (config persistence)
If ~/.config/fast-ship/config.yaml does not exist,mustask the user for the following information:
- Base URL: Fast Ship service address, e.g.
http://localhost:8080orhttps://fast-ship.example.com - API Key: a key created from the Fast Ship web interface under "Settings → API Key" (
fsk_starting with)
Then write the configuration to the file:
# ~/.config/fast-ship/config.yaml
base_url: "https://fast-ship.example.com"
api_key: "fsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Note:
~/.configthe directory usually already exists on macOS/Linux; if it does not, create the directory first.
Subsequent use
The script reads ~/.config/fast-ship/config.yaml from base_url and api_key,no longer asks the user。Agent do notRead the Key out and write it into a command or output.
Request headers
The script automatically carries the following request headers for all requests (no need to add them manually):
Authorization: Bearer <api_key>
Content-Type: application/json; charset=utf-8
Core workflow
When the source is not specified, create source: "internal". Use source: "github"only when the user explicitly requests syncing GitHub. An API Key can only create internal.
For fields, always see references/api.md; only the endpoints are kept here.
GET /api/projects, determineproject_id。- before tagging, first
GET /api/projects/{id}/issues/repo-labels. When the project has no GitHub repository configured or the labels are empty, changeGET /api/projects/{id}/issues/filter-options。 POST /api/projects/{id}/issuesto create. For subsequent paths, only use theid(UUID) from the response. The page given to the user is{base_url}/projects/{project_id}/issues/{id}。- To tag,
PUT /api/issues/{iid}, and the label name must already exist. Do not rely on theworkflow_statusin the create request to advance progress. - To advance, use
PUT /api/issues/{iid}/internal-meta, with the value beingtodo、in_progressordone。 - To query one, use
GET /api/issues/{iid}. For a list, useGET /api/projects/{id}/issues。
Screenshot annotations (AI review loop)
The rectangular box-selection annotations drawn on the canvas of the screenshot page are attached toa specific screenshot versionfor humans/AI to exchange review comments. An API Key can read all annotations, pull cropped images, and mark as resolved; it cannot create/delete annotations (40301), and changing text/linking an Issue/reopening also requires a JWT.
- List:
GET /api/projects/{id}/screenshot-annotations→{items:[...]}, and the query can be filtered bystatus(open/resolved)、issue_id、screen_id. - Issue dimension:
GET /api/issues/{iid}'sscreenshot_annotations[]embeds the annotations linked to that Issue (omitted if none). - Each annotation carries:
image_url(the original image of the version it is in),crop_url(a cropped image of the box-selected area, PNG, with margins on all sides), proportional coordinatesx/y/width/height(0~1), pixel coordinatespixel_rect(null when the size is unknown),screen_key/screen_title/screen_group、is_latest_version、status。 crop_url/image_urlis the<img>direct-link path: the script only needs to include the Authorization header when calling; only the Web side's<img>requires?token=<jwt>。- Mark as resolved:
PUT /api/screenshot-annotations/{aid}, and the body as a whole passes only{"status":"resolved"}—including other fields oropenwill result in 40301. - When the original image exceeds the decoding pixel limit (about 64MP), crop returns 40004 (
尺寸超出可处理范围); in this case, useimage_urlto pull the original image and scale it yourself.
AI review loop (for field details, see the screenshots chapter of api.md):
GET /api/issues/{iid}Readscreenshot_annotations, or filter project annotations byissue_id/status=open.- By
crop_urlpull the cropped image to see which area the user box-selected and what comment they wrote. - After modifying the UI,
POST /api/projects/{id}/screenshotsupload a new screenshot withthe samescreen_key(it is automatically grouped into the same interface as a new version; old annotations remain attached to the old version, andis_latest_versionbecomes false). - Mark each
PUT {"status":"resolved"}as resolved one by one.
Deleting an Issue only clears the annotation's issue_id(the annotation is retained); deleting a screenshot version/interface cascades to delete annotations.
Issue linked to a PR
One Issue can have multiple PRs attached (cross-repository is also possible).Timing: attach immediately after creating the PR so that implementation progress can be seen on the Issue page; after the PR is merged, call sync once to refresh the status.
- attach: the body passes only
{"url":"https://github.com/<owner>/<repo>/pull/<n>"}, supporting/files, query, and other suffixes. The server fetches the title/status/author from GitHub on its own; if the fetch fails, no record is written (502). Repeated attach is idempotent and does not produce a second row. - Read: details
GET /api/issues/:iidofpull_requests[]is the full list; list items carrypull_request_summary {total, open, merged}(closed = total - open - merged)。 - sync: returns
{items, failures}, each PR is an independent failure domain—if one cannot be fetched (404/rate limit) it only goes intofailures[], and the rest refresh as usual;failures[].idis the associated row id. - detach:
DELETE /api/issues/:iid/pull-requests/:lid; a nonexistent association returns 40412 (not idempotent; repeated calls will error). - PR status and
workflow_statusare not coupled: all PRs merged does not mean the requirement is complete,internal-metastill has to be advanced on its own. link_origin=manualassociations are never mistakenly deleted by the cleanup logic of the GitHub sync projection.
Semantic pitfalls and permission division of labor
multipart
These endpoints are curl -F file=@路径, not JSON. The Issue image response contains markdown; only by pasting this into the body is the attachment actually attached.
POST /api/auth/avatarPOST /api/projects/{id}/issues/assets(draft, JWT only)POST /api/issues/{iid}/assetsPOST /api/versions/{vid}/artifacts(optional form fieldplatform)
Field-level permissions
The spec's security often lists both JWT and API Key, but the real rejection happens in the handler.
- API Key cannot pass
state/state_reason, cannotPOST /api/issues/{iid}/comments, cannotPUTorDELETE /api/issues/{iid}/ship-hook. Doing so results in 403 (40301).ship_hookonly appears in the Issue's GET and list, read-only. PUT /api/issues/{iid}/collab/consensus、PUT /api/issues/{iid}/collab/summary、PUT /api/issues/{iid}/recommendationonly accepts API Key. JWT calls return 403 (40303).- After a recommendation is deferred by the user,
PUT recommendationreturns 409 (40911)—do not retry recommending, and do not try to DELETE (also 40911).PUT/DELETE /api/issues/{iid}/recommendation/deferis defer/restore, JWT only (API Key 40301). Completely removing a deferred item can only be done by a JWT user. POST /api/projects/{id}/logsonly accepts API Key. JWT calls return 403 (40303).
checklist is a whole-group replacement
PUT /api/issues/{iid}/checklist submitted items is the final list. Old entries not included will be deleted.items=[] will clear the checklist.
q is an in-memory full filter
GET /api/projects/{id}/issues of q is not an index query. It scans the full set, matching title, body, author login,INT-xxx / GH-xxx. Pure digits will also match sequence_number or the GitHub number.
Details come with collab
GET /api/issues/{iid} data contains collab(consensus and summary). List items do not. To see the collaboration area, just read the details; there is no need to call GET /collab。
Batch workflow changes
PUT /api/issues/internal-meta request body is {items:[{issue_id, workflow_status}]}, at most 200 entries. If one entry fails, the whole batch does not become 4xx; HTTP remains 200, see failures[]。
collab read before write
PUT Before consensus or summary, GET first. Do not write back blocks the user has already cleared.DELETE is idempotent; if the target never existed, it also returns 200.
Do not write consensus before the follow-up questions are finished. After the decisions are all asked and the user confirms work can start, then PUT the content that has been decided. If the user says stop asking and just do it, leave the consensus empty; do not make up "implement according to the original Issue text." Do not close the ticket without permission, and do not commit or push code.
Write the completion summary for ordinary people to read; two paragraphs are enough: how it was before, how it is now. Do not list files, do not paste commit SHAs, do not write a test checklist. If the changes have not yet landed on a perceptible result, then do not write "now" yet.
Request encoding
Write the request body as a UTF-8 JSON file, using the script's body.json Passed in. Do not concatenate Chinese in the shell.
Breaking Changes
- API Key Not allowedvia
PUT /api/issues/:issue_idto modifystate/state_reason(open/close); the Agent should instead usePUT /api/issues/:issue_id/internal-meta'sworkflow_statusto mark completion; open/close is operated by the JWT user on the Web side. - API Key Not allowedpost comments (
POST /api/issues/:issue_id/comments)。 - API Key Not allowed
PUT/DELETE /api/issues/:issue_id/ship-hook。ship_hookonly appears in Issue GET/list for read-only. After shipment, closing the order/leaving a message is configured by the JWT user on the Web, and executed by the server after successful shipment. - Legacy collaboration endpoint
POST/PUT/DELETE /collab/notes、/collab/questionshas been removed;suggestions、plan、reviewandcommit_idsare both deprecated. The currentGET /collabonly returnsconsensusandsummary。