Factuarea APIDevelopers

Projects and tasks

Projects with keys and board columns, tasks with positions, labels, comments, relations, custom fields, links to documents and attachments — the work-management API under /v1/projects and /v1/tasks.

Factuarea includes a native task module: projects with a board of columns, and tasks that can be linked to the invoices, quotes, contacts and other documents of your company. The API exposes it under /v1/projects, /v1/tasks, /v1/task-labels, /v1/task-timers, /v1/users, /v1/notifications and /v1/agenda. Every operation is also an MCP tool and a CLI command.

Tasks need the tasks module, which every plan includes, and the fine-grained scopes projects:read|write|delete, tasks:read|write|delete, users:read and notifications:read|write. Issuing a key with those scopes for a company without the module is rejected with scope_not_allowed_by_plan. The projects:delete and tasks:delete scopes are API-key-only: an OAuth consent never grants them. See Scopes & irreversibility.

Projects and keys

A project groups tasks and owns the board they live on. Its key — one uppercase letter followed by up to nine uppercase letters or digits, such as DEV — prefixes the key of every task: DEV-1, DEV-2… Keys are unique in your company, ignoring case and counting archived projects. A key already in use returns 409 project_key_in_use.

curl -X POST https://api.factuarea.com/v1/projects \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Web redesign",
    "key": "WEB",
    "icon": "rocket",
    "description": "Storefront redesign and follow-up fixes."
  }'

The project is created with its default board columns and comes back with its task statistics (total_tasks, completed_tasks, completion_percentage and next_due_on).

  • GET /v1/projects lists the projects in the order the app shows them. Archived projects stay out unless you pass include_archived=true.
  • POST /v1/projects/find-by-key resolves a project from its key (WEB), ignoring case and archived or not. It is a read: it needs no Idempotency-Key.
  • PUT /v1/projects/{project} is partial. Changing the key keeps the previous one as an alias, so tasks keep answering to their old keys. The billing defaults used to invoice logged time live here too — see Time logged on tasks.
  • POST /v1/projects/{project}/archive and .../unarchive are reversible: an archived project leaves the default listings and stops accepting new tasks (422 project_archived), but everything stays readable.
  • DELETE /v1/projects/{project} is permanent and takes the project's tasks, comments, attachments and logged time with it.

Columns and board status

A task is always in one of three board states:

statusWhere the task iscolumn_id
plannedThe backlog: waiting to be schedulednull
activeIn a column of the boardThe column
archivedArchived, out of the boardnull

planned and archived are virtual states, not columns, and their names are reserved: a column cannot use them as its slug (422 column_slug_reserved).

A new project gets four columns — to do, in progress, in review and done — named in the language of the user who creates it, with the canonical slugs to-do, in-progress, in-review and done. Only the last one is final (is_final: true): moving a task into a final column completes it (completed_at is set), and moving it out reopens it.

  • POST /v1/projects/{project}/columns adds a column at the end of the board. Its slug derives from the name and is unique in the project (409 column_slug_in_use). color is a palette key (gray, red, orange, amber, green, teal, blue, cyan, violet or pink) or a hexadecimal code.
  • PUT .../columns/{column} is partial. Turning a column final completes the tasks it holds; turning it back reopens them.
  • PUT .../columns/reorder takes column_ids with every column of the project exactly once, in the new order.
  • Columns come back as a plain list, without pagination.

Deleting a column that has tasks

Deleting a column is permanent, and a column with tasks needs somewhere to send them. Pass the destination column of the same project in the move_to_column_id query parameter:

curl -X DELETE \
  "https://api.factuarea.com/v1/projects/$PROJECT_ID/columns/$COLUMN_ID?move_to_column_id=$OTHER_COLUMN_ID" \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"

In one transaction the tasks are appended to the end of the destination column, in their order, each one emits its task.status_changed event, and the column is deleted. Without move_to_column_id, a column that still has tasks returns 409 column_has_tasks and nothing changes; a destination that does not exist, belongs to another project or is the column being deleted returns 422 invalid_move_target_column. An empty column needs no destination. The operation is irreversible, so Idempotency-Key is required.

Tasks, keys and the board

project_id and title are the only required fields of POST /v1/tasks. A task starts in the first column of its project unless you send a column_id or a virtual status. The optional fields are description (Markdown), priority (none, low, medium, high or urgent; none by default), start_on and due_on (YYYY-MM-DD; a start_on after due_on returns 422 invalid_task_schedule), assignee_id (a member of your company), label_ids, custom_fields and entity_link.

curl -X POST https://api.factuarea.com/v1/tasks \
  -H "Authorization: Bearer $FACTUAREA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "0193a4f2-7c20-7a11-8b52-4d6e8f0a2c01",
    "title": "Call the customer about the checkout demo",
    "priority": "high",
    "due_on": "2026-10-15",
    "assignee_id": "0193a4f2-6b1c-7d3e-8a41-2c5e7f9a1b02",
    "label_ids": ["0193a4f2-9a64-7e55-8f96-8b0c2d4e6a02"],
    "custom_fields": { "0193a4f2-7e42-7c33-8d74-6f8a0b2c4e01": 4 },
    "entity_link": { "type": "quote", "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a4d" }
  }'

The task comes back with its key (DEV-16), its number in the project, its board status and column_id, its labels, its custom field values and counters for subtasks, comments and attachments. The number only grows: a deleted task never frees its number for reuse.

Resolve a task from its key

People talk about DEV-12, not about a UUID. POST /v1/tasks/find-by-key takes { "key": "DEV-12" }, ignoring case, and returns the task. Previous keys keep working: a task moved from DEV-12 to another project, or whose project changed its key, is still found by the old key. A malformed key returns 422 invalid_task_key and an unknown one 404 task_not_found. The q filter of the search accepts a key too.

GET /v1/tasks searches across projects:

curl "https://api.factuarea.com/v1/tasks?project_id=$PROJECT_ID&status=active&assignee_id=me&sort=due_on&limit=50" \
  -H "Authorization: Bearer $FACTUAREA_API_KEY"

The filters are q, project_id, status, column_id, priority, assignee_id (a member UUID or me), label_id, due_before, due_after and completed. sort accepts created_at, updated_at, due_on, priority or number, with a - prefix for descending.

Status, position and movement

  • POST /v1/tasks/{task}/status moves a task to a column_id of its project or to a virtual status, at the end of the destination.
  • POST /v1/tasks/{task}/reposition places it at an exact position: an index (0 is first) or a neighbour, before_task_id or after_task_id, and optionally another column in the same call.
  • POST /v1/tasks/{task}/move sends it to another project. The task gets a new key there and keeps answering to the previous one; custom field values the destination does not define are dropped.
  • POST /v1/tasks/{task}/duplicate copies it to the end of the same column. The copy keeps description, priority, dates, labels, custom field values and assignee; comments, activity, time entries and links are not copied.
  • POST /v1/tasks/{task}/assign and .../unassign set and clear the assignee. Find member ids with GET /v1/users; a user who is not a member returns 422 task_assignee_not_member.

Edit and delete

PUT /v1/tasks/{task} is partial: only the fields you send change, and an explicit null clears description, start_on, due_on and assignee_id. priority and title cannot be null.

DELETE /v1/tasks/{task} is permanent. It takes the task's comments, attachments, relations and time entries with it, and it is irreversible: Idempotency-Key is required.

Bulk operations

Three operations act on up to 200 tasks at once, all or nothing: POST /v1/tasks/bulk-status, POST /v1/tasks/bulk-update (priority, assignee, due date and one label to add or remove) and POST /v1/tasks/bulk-delete. All three require an Idempotency-Key. A larger selection returns 422 bulk_task_selection_too_large; tasks that no longer exist are skipped and the result reports how many changed. See Bulk operations.

Labels, comments, relations and custom fields

Task labels live in a catalog per company and are unrelated to the tags of documents: see Tags and custom fields for those. A label has a name, unique in your company ignoring case (409 task_label_name_in_use), and a color from the palette or a hexadecimal code. The catalog lists each label with its tasks_count: how many tasks carry it, archived ones included. POST /v1/tasks/{task}/labels adds a label to a task and DELETE .../labels/{label} removes it; deleting a label from the catalog removes it from every task.

Comments are Markdown, from 1 to 10,000 characters. Mentions of members of the company notify the people mentioned; mentions of anyone else are ignored. The author of a comment has a type: user, external (synchronized from a code forge), imported or automation. Only the author can edit a comment, and comments synchronized or imported cannot be edited (422 task_comment_not_editable); the author and the owners and admins of the company can delete one.

Relations join two tasks of the company as subtask, blocks or related. A task cannot relate to itself (422 task_self_relation), a relation that would close a cycle is rejected (422 task_relation_cycle) and a duplicate returns 409 task_relation_exists. The list shows each relation from the point of view of the task in the path, through direction: parent, child, blocks, blocked_by or related. Deleting a relation leaves both tasks untouched.

Task custom fields are defined per project, with a type of text, number, date, dropdown, boolean or multiselect, and they are not the custom fields of documents. A required field needs a non-empty default value, which existing tasks receive. Set values when you create the task, with the custom_fields map (field id to value), or later with PUT /v1/tasks/{task}/custom-fields/{field}; null clears an optional field. A value that does not fit the type returns 422 invalid_custom_field_value, and a definition that contradicts itself, or an edit that would strand existing values, 422 invalid_custom_field_definition. Deleting a definition deletes its values in every task of the project.

A task can point at the rest of Factuarea. The vocabulary of linkable entities is closed: invoice, quote, proforma, delivery_note, purchase_invoice, recurring_invoice, contact, product and employee.

  • At creation, entity_link links the task in the same transaction. If the entity does not exist, belongs to another company or its module is not accessible, the call returns 404 linked_entity_not_found and the task is not created.
  • Afterwards, POST /v1/tasks/{task}/entity-links links it; linking the same entity again returns the existing link. A link to an entity that was deleted stays in the list with available: false.
  • From the entity, GET /v1/tasks/linked?entity_type=invoice&entity_id=… lists the tasks linked to an invoice, a quote or any other entity, archived ones flagged.
  • External links — POST /v1/tasks/{task}/external-links — attach a plain http or https address. Links that mirror an issue, a pull request or a branch of a code forge (GitHub, GitLab or Gitea) are kept by the integration.

Attachments and upload links

POST /v1/tasks/{task}/attachments uploads a file as multipart/form-data. The maximum is 10 MB per file, and the type is checked by content, never by extension: images (PNG, JPEG, GIF, WebP, HEIC, AVIF), PDF, Word, Excel and PowerPoint documents, OpenDocument text and spreadsheets, plain text, CSV, Markdown and ZIP. target places the file in a new comment (comment, the default) or at the end of the description (description), and note becomes the comment text. Files count towards the storage quota of the plan (402 storage_quota_exceeded when it is full) and the upload needs an Idempotency-Key.

To receive a photo from a phone, or a file from someone without an account, create an upload link with POST /v1/tasks/{task}/upload-links. The link is single-use, expires after 30 minutes, and its url is shown only in that response. Once it is used or expired it answers 410 task_upload_link_expired.

Attachments are listed, read and downloaded per task, and DELETE .../attachments/{attachment} removes the file for good and frees its storage.

Activity

GET /v1/tasks/{task}/activities returns the activity log, newest first: status and column changes, edits with the changed fields, assignments, labels, comments, relations, attachments and logged time, each with its actor (user, api_key, external or system). Entries have no id, so the cursor is opaque: pass the next_cursor back as starting_after untouched.

Pagination, partial updates and errors

  • Cursor pagination. Lists of projects, tasks, labels, comments, time entries, users, notifications and activity return { data, has_more, next_cursor }; pass next_cursor as starting_after for the next page. Columns, custom field definitions, relations, links and attachments are short and come back as a plain list. See Pagination.
  • PUT is partial everywhere: omitted fields keep their value, and an explicit null clears the optional ones.
  • Idempotency. The nine irreversible operations, the bulk operations, the import and the file upload require an Idempotency-Key; every other write accepts it. See Idempotency.
  • Errors. Every task error has a stable code, listed under Tasks error codes.

Some parts of the module exist only in the app and have no operation in the API: the integrations with code forges and chat tools, the iCal feeds (see the iCal feed guide), the read-only public board, the project background, the order of the projects and the notification preferences.

Next steps

On this page

Need a hand?Contact support