links, text and files, in several places at once, with tags
Attachments
An attachment is a link, a piece of text, a file or a picture. It belongs to one or more places: to-dos, areas, projects, subprojects and notes, in any mix. It is ONE attachment and one stored file wherever it shows, never a copy.
Places
The first place is the attachment's primary place: parentType and parentId on the attachment. Every other place is listed after it in places, oldest first:
{ "id": "9C3F1A72-…", "kind": "url", "url": "https://example.com/manual",
"parentType": "todo", "parentId": "41D0…",
"places": [
{ "parentType": "todo", "parentId": "41D0…", "sortKey": "a5", "placeId": null, "primary": true },
{ "parentType": "container", "parentId": "8C1E…", "sortKey": "a2", "placeId": "E7B2…", "primary": false }
], "…": "…" }container means an area, a project or a subproject.
Attach it somewhere else too
POST /v1/attachments/{id}/places adds a place, at the end of that place's list. A place it already has changes nothing and answers 200.
curl -s "https://api.inittasks.com/v1/attachments/$ATT/places" \
-H "Authorization: Bearer $INITTASKS_TOKEN" -H "Content-Type: application/json" \
-d "{\"parentType\": \"container\", \"parentId\": \"$PROJECT\"}"The place must exist and not be in the trash.
Take it out of one place
DELETE /v1/attachments/{id}/places/{parentId} removes one place, as the apps' detach does:
- another place: it leaves that place only;
- its primary place, while it has others: the oldest other place becomes the primary;
- its last place: it becomes an inbox item holding the same link, text or file, and the attachment row goes. Its tags stay behind, because a capture has no tags. This case needs
inbox:writeas well.
The answer says which happened: result is removed (with the attachment) or inbox (with the new capture). An image or file that has not finished uploading cannot leave its last place from here: its bytes are on the device that added it, so the call is conflict.
Order
Each place keeps its own order. GET /v1/attachments?parent_id=<place> lists the attachments IN a place, primary or not, in that place's order. POST /v1/attachments/{id}/reorder moves one within one place: parentId names the place, and after, before or step (up, down) says where. It never moves the attachment in its other places.
Tags
An attachment carries tags, by name, like a to-do. Set them on create or with PATCH /v1/attachments/{id} (the whole set; [] clears it). A new name creates the tag. GET /v1/attachments?tag=<name> lists the attachments carrying a tag (repeat it for several). Renaming or deleting a tag rewrites it here too. Saved filters match to-dos only, never attachments.
Creating and editing
POST /v1/attachments creates a url or a text attachment on a live to-do, container or note, at the end of its list. PATCH changes title, url (a link), text (a text attachment) and tags. A file keeps its extension in its title; keep it when you rename one.
Files and pictures
POST /v1/attachments/upload stores a file. Send the file itself as the body, and say where it goes and what it is called in the query:
curl -s "https://api.inittasks.com/v1/attachments/upload?parent_type=container&parent_id=$PROJECT&name=invoice.pdf" \
-H "Authorization: Bearer $INITTASKS_TOKEN" -H "Content-Type: application/pdf" \
--data-binary @invoice.pdfparent_typeistodo,containerornote,parent_idis the place, andnameis the filename WITH its extension. Optional:mime, atag(repeat it for several) and your ownid.- The body is the file, with its
Content-Length(--data-binary @filesends it). ItsContent-Typeis the file's type;application/octet-stream(or curl's default) leaves the type tomimeor the extension. Onlyapplication/jsonis read as the JSON form below, so send a.jsonfile asapplication/octet-stream. - A file is at most 50 MB, the apps' own limit. A bigger one is payload_too_large, answered from its
Content-Lengthbefore it is read.
The server seals the bytes under a key of their own as they arrive, stores them as you, and makes the attachment with its file in one write. An image/* type makes a picture; anything else is a file.
A small file can also go as JSON, base64 in content with the filename in name, as long as the whole body stays under 1 MB (about 750 KB of file):
curl -s https://api.inittasks.com/v1/attachments/upload \
-H "Authorization: Bearer $INITTASKS_TOKEN" -H "Content-Type: application/json" \
-d "{\"parentType\": \"container\", \"parentId\": \"$PROJECT\", \"name\": \"notes.txt\", \"content\": \"$(printf 'hello' | base64)\"}"- The server makes no thumbnail. A picture shows its name in lists until it is opened, unless you send
thumbnailin the JSON form: a JPEG of at most 256 px and 64 KB, base64. GET /v1/attachments/{id}/filereads the decrypted bytes of any file or picture. A file added in an app before its upload finished readspending: trueand has no bytes yet.
A file straight into the inbox
POST /v1/inbox/upload is what sharing a file or a picture to init.Tasks does: a capture that holds the file, at the end of the inbox, to sort later. The same two forms, with only name (and optionally mime, id) in the query:
curl -s "https://api.inittasks.com/v1/inbox/upload?name=invoice.pdf" \
-H "Authorization: Bearer $INITTASKS_TOKEN" -H "Content-Type: application/pdf" \
--data-binary @invoice.pdfIt needs inbox:write. The capture's kind is image or file, its bytes read back at GET /v1/inbox/{id}/file, and POST /v1/inbox/{id}/move files it onto a to-do or a container: the file moves to the attachment it becomes.
Deleting
DELETE /v1/attachments/{id} deletes the attachment from every place, with its stored file. It is permanent: attachments have no trash.
Permanently deleting a to-do, a container or a note releases its attachments instead of deleting them blindly. An attachment that is also somewhere else moves there (promoted, if this was its primary place). Only an attachment whose every place is deleted goes with it, and then its stored file goes too. See trash.
Events
An additional place is a row of its own. Adding one is attachment_place.created, reordering inside it is attachment_place.updated, and removing it is attachment_place.deleted. data.id is the place row's id: GET /v1/attachment-places/{id} says which attachment and which place, while it exists. A promotion also changes the attachment row (attachment.updated). See webhooks.
MCP
| task | tool |
|---|---|
| list (in a place, by tag) | list_attachments |
| add a link or text | add_attachment |
| add a file (text you wrote, or base64, up to 2 MB) | upload_attachment |
| put a file in the inbox (the same, up to 2 MB) | capture_file |
| rename, change, tag | update_attachment |
| attach to another place | attach_to |
| take out of one place | detach_attachment |
| reorder within a place | move_attachment |
| delete everywhere | delete_attachment (needs confirm: true) |
The tools take an attachment by id, exact title or url, and a place by id or exact name. A file over 2 MB goes through the REST routes above, up to 50 MB.