Skip to main content

Projects

Projects group documents (invoices, offers, letters, reminders, order confirmations, incoming invoices) under a single named container. Use them to track work-in-progress for a customer, attach metadata like place and ETA, and surface aggregated status downstream.

Each project can hold at most one document; each document can belong to at most one project.


Endpoints

All project endpoints require apiKeyAuth.

EndpointMethodDescription
/projectsGETPaginated list of projects.
/projectPOSTCreate a new project.
/project/{id}GETGet a single project by id.
/project/{id}PATCHPartially update a project.
/project/{id}DELETEDelete a project. Returns { "succeed": true }.
/project/{id}/documentPOSTAttach a document (invoice, offer, …) to the project.
/project/{id}/documentDELETEDetach the document from the project. Returns { "succeed": true }.

{id} is the project UUID.


Project object

FieldTypeDescription
iduuidProject ID.
namestring (≤ 255 chars)Project display name. Required on create.
descriptionstring | nullFree-text description.
placestring (≤ 255) | nullFree-text location.
etaISO 8601 date-time | nullEstimated time of arrival / completion.
estMinutesinteger ≥ 0 | nullEstimated effort in minutes.
currencystring | nullISO 4217 currency code (e.g. EUR).
statusenum | nullactive, paused, or closed.
customerIduuid | nullLinked customer.
customerobject | nullEmbedded { id, name } when customerId is set.
documentobject | nullEmbedded document reference (see below) when a document is attached.
createdAtISO 8601 date-timeCreation timestamp.
updatedAtISO 8601 date-timeLast update timestamp.

The embedded document object carries one populated id (the rest are null):

{
"id": "<assignment uuid>",
"invoiceId": null,
"invoiceIncomingId": null,
"offerId": null,
"letterId": null,
"reminderId": null,
"orderConfirmationId": null
}

Create a project

Only name is required. Any other field can be omitted or sent as null.

Example: Minimal create
curl -X POST \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{ "name": "Berlin warehouse rollout" }' \
https://api.faktoora.com/api/v1/project

Response (200 OK)

{
"id": "6716007f-26fc-430d-bba6-3aea11d271b7",
"name": "Berlin warehouse rollout",
"description": null,
"place": null,
"eta": null,
"estMinutes": null,
"currency": null,
"status": null,
"customerId": null,
"customer": null,
"document": null,
"createdAt": "2026-05-26T13:41:55.073Z",
"updatedAt": "2026-05-26T13:41:55.073Z"
}
Example: Full create
curl -X POST \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "Berlin warehouse rollout",
"description": "Q3 fit-out, 8 racks",
"place": "Berlin",
"estMinutes": 120,
"currency": "EUR",
"status": "active",
"customerId": "8c5a3f0c-1234-4abc-9def-0123456789ab"
}' \
https://api.faktoora.com/api/v1/project

List projects

GET /projects returns a paginated envelope.

Query parameters (all optional):

ParamTypeDescription
pageinteger (≥ 1)Page number.
perPageinteger (1–100)Items per page (default 25).
keywordstringFree-text match against name / description.
statusrepeated stringFilter by one or more status values. Repeat the param for multiple values.
customerIdrepeated uuidFilter by one or more customer IDs.
sortenumname, createdAt, or updatedAt.
orderenumasc or desc.
Example: List active projects for a customer
curl -H "X-API-KEY: your-api-token" \
"https://api.faktoora.com/api/v1/projects?status=active&customerId=8c5a3f0c-1234-4abc-9def-0123456789ab&sort=createdAt&order=desc"

Response (200 OK)

{
"data": [ /* Project objects */ ],
"meta": { "page": 1, "perPage": 25, "totalItems": 1, "totalPages": 1 }
}

Update a project

PATCH /project/{id} accepts the same fields as create, all optional. Only the fields you send are updated.

Example: Close a project
curl -X PATCH \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{ "status": "closed" }' \
https://api.faktoora.com/api/v1/project/6716007f-26fc-430d-bba6-3aea11d271b7

The response is the updated Project object.


Delete a project

DELETE /project/{id} permanently removes the project. The attached document (if any) is left untouched but is automatically detached.

curl -X DELETE \
-H "X-API-KEY: your-api-token" \
https://api.faktoora.com/api/v1/project/6716007f-26fc-430d-bba6-3aea11d271b7

Response (200 OK)

{ "succeed": true }

Attach / detach a document

Use POST /project/{id}/document to link a document to a project, and DELETE /project/{id}/document to unlink it.

Both endpoints take the same body:

FieldTypeDescription
documentIduuidThe document's UUID.
documentTypeenumOne of invoice, invoiceincoming, letter, offer, reminder, orderconfirmation.

The project must be empty before a new document can be attached (one document per project).

Example: Attach an invoice
curl -X POST \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"documentId": "550e8400-e29b-41d4-a716-446655440000",
"documentType": "invoice"
}' \
https://api.faktoora.com/api/v1/project/6716007f-26fc-430d-bba6-3aea11d271b7/document

Response (200 OK)

{
"id": "ddc7c64b-7c11-4a6a-9d40-7a08cba2c5e1",
"projectId": "6716007f-26fc-430d-bba6-3aea11d271b7",
"documentId": "550e8400-e29b-41d4-a716-446655440000",
"documentType": "invoice"
}
Example: Detach a document
curl -X DELETE \
-H "X-API-KEY: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"documentId": "550e8400-e29b-41d4-a716-446655440000",
"documentType": "invoice"
}' \
https://api.faktoora.com/api/v1/project/6716007f-26fc-430d-bba6-3aea11d271b7/document

Response (200 OK)

{ "succeed": true }

Errors

StatusWhen
400Validation error — e.g. missing name, invalid status, malformed UUID.
401Missing or invalid X-API-KEY.
404Project ID not found, or referenced customerId / documentId does not exist.

Error body:

{
"code": "E_VALIDATION",
"statusCode": 400,
"message": "body must have required property 'name'"
}