Projects API
Projects (Projekte) ermöglichen es Ihnen, Kundenprojekte zu verwalten und die Abrechnung zu organisieren. Jedes Projekt ist einem Customer zugeordnet und kann Zeiterfassungen, Ausgaben, Aufgaben, Rechnungen und Offerten enthalten.
Project-Objekt
{
"data": {
"id": 55,
"name": "Website Relaunch",
"notes": "Komplette Überarbeitung der Firmenwebsite inkl. neuem Design",
"archived": false,
"budget": 15000.00,
"user_id": 456,
"customer_id": 410,
"project_type": "fixedBudget",
"kanban_status": "In Progress",
"start_date": "2024-01-15",
"end_date": "2024-03-31",
"formatted_start_date": "15.01.2024",
"formatted_end_date": "31.03.2024",
"hourly_rate": 150.00,
"entries_count": 12,
"invoices_count": 2,
"proposals_count": 1,
"tasks_count": 8,
"times_count": 45,
"budget_usage": {
"no_budget": false,
"billable_time_total": "45:30",
"non_billable_time_total": "05:15",
"billable_sum": "6,825.00",
"total_time": "50:45",
"hourly_rate": "150.00",
"total_sum_invoiced": "5,000.00",
"total_sum_open": "1,825.00",
"used_percentage": 52.5,
"used_chf": "7,875.00",
"total_budget": "15,000.00",
"remaining": "7,125.00",
"remaining_percentage": 47.5,
"used_string": "7,875.00 CHF / 15,000.00 CHF (52.5%)",
"billability": 86.67
},
"open_times": {
"readable": "15:45",
"sortable": 945
}
}
}Attribute
| Feld | Typ | Beschreibung |
|---|---|---|
id | integer | Eindeutige Project-ID |
name | string | Name des Projekts (erforderlich) |
notes | string|null | Notizen zum Projekt |
archived | boolean | Ob das Projekt archiviert ist |
budget | decimal|null | Projektbudget (für Budget-basierte Projekte) |
user_id | integer | ID des Benutzers, der das Projekt erstellt hat |
customer_id | integer | ID des zugehörigen Customers |
project_type | string | Projekttyp: byHour, fixedBudget, fixedPrice |
kanban_status | string|null | Kanban-Status (z.B. "To Do", "In Progress", "Done") |
start_date | date|null | Startdatum (Format: YYYY-MM-DD) |
end_date | date|null | Enddatum (Format: YYYY-MM-DD) |
formatted_start_date | string|null | Formatiertes Startdatum (dd.mm.yyyy) |
formatted_end_date | string|null | Formatiertes Enddatum (dd.mm.yyyy) |
hourly_rate | decimal | Stundensatz für das Projekt |
entries_count | integer | Anzahl Buchungen im Projekt |
invoices_count | integer | Anzahl Rechnungen für das Projekt |
proposals_count | integer | Anzahl Offerten für das Projekt |
tasks_count | integer | Anzahl Aufgaben im Projekt |
times_count | integer | Anzahl Zeiterfassungen im Projekt |
budget_usage | object | Detaillierte Budget-Nutzungsstatistik |
open_times | object | Offene (nicht verrechnete) Zeiten |
Optionale Verknüpfungen
Diese Felder können via include Parameter geladen werden:
| Feld | Typ | Beschreibung |
|---|---|---|
customer | object | Zugehöriger Customer |
invoices | array | Rechnungen des Projekts |
proposals | array | Offerten des Projekts |
tasks | array | Aufgaben des Projekts |
open_times_list | array | Liste offener Zeiterfassungen |
expenses | array | Ausgaben des Projekts |
times | array | Alle Zeiterfassungen |
entries | array | Alle Buchungen |
Projekttypen
| Type | Wert | Beschreibung |
|---|---|---|
| By Hour | byHour | Stundenbasierte Abrechnung (Standard) |
| Fixed Budget | fixedBudget | Festes Budget, Zeit wird getrackt |
| Fixed Price | fixedPrice | Festpreis, unabhängig von Zeit |
Endpoints
Alle Projects auflisten
GET /companies/{company}/projectsRuft eine paginierte Liste aller Projects der Firma ab.
Parameter
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
page | integer | 1 | Seitennummer |
per_page | integer | 15 | Anzahl Projects pro Seite (max. 100) |
filter[archived] | boolean | - | Filtern nach Archivierungsstatus (exakte Übereinstimmung) |
filter[customer_id] | integer | - | Filtern nach Customer-ID (exakte Übereinstimmung) |
include | string | - | Zugehörige Daten laden: customer (kommagetrennt) |
Response
{
"data": [
{
"id": 55,
"name": "Website Relaunch",
"notes": null,
"archived": false,
"budget": 15000.00,
"user_id": 456,
"customer_id": 410,
"project_type": "fixedBudget",
"kanban_status": "In Progress",
"start_date": "2024-01-15",
"end_date": "2024-03-31",
"formatted_start_date": "15.01.2024",
"formatted_end_date": "31.03.2024",
"hourly_rate": 150.00,
"entries_count": 12,
"invoices_count": 2,
"proposals_count": 1,
"tasks_count": 8,
"times_count": 45,
"budget_usage": {...},
"open_times": {...}
}
],
"links": {
"first": "https://app.milkee.ch/api/v2/companies/123/projects?page=1",
"last": "https://app.milkee.ch/api/v2/companies/123/projects?page=3",
"prev": null,
"next": "https://app.milkee.ch/api/v2/companies/123/projects?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 3,
"path": "https://app.milkee.ch/api/v2/companies/123/projects",
"per_page": 15,
"to": 15,
"total": 37
}
}Beispiele
# Alle Projects abrufen
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects"
# Nur aktive Projects
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects?filter[archived]=false"
# Projects eines bestimmten Customers
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects?filter[customer_id]=410"
# Mit Customer-Daten
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects?include=customer"Neues Project erstellen
POST /companies/{company}/projectsErstellt ein neues Projekt für die angegebene Firma.
Request Body
{
"name": "Website Relaunch",
"customer_id": 410,
"project_type": "fixedBudget",
"budget": 15000.00,
"hourly_rate": 150.00
}Validierung
| Feld | Regeln |
|---|---|
name | required, string, min:1, max:255 |
customer_id | required_without:newCustomerName, nullable, integer, exists:customers,id, prohibits:newCustomerName |
newCustomerName | required_without:customer_id, nullable, string, min:1, max:255 |
hourly_rate | nullable, numeric, min:0, max:999999.99 |
budget | nullable, numeric, min:0, max:9999999.99 |
project_type | nullable, einer von: byHour, fixedBudget, fixedPrice (Standard: byHour) |
Besonderheiten
- Customer erstellen: Mit
newCustomerNamewird automatisch ein neuer Customer angelegt - Customer-Auswahl: Entweder
customer_idodernewCustomerNamemuss angegeben werden - Standard-Projekttyp: Wenn nicht angegeben, wird
byHourverwendet - Stundensatz: Wird auf 0 gesetzt wenn null
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": 55,
"name": "Website Relaunch",
"notes": null,
"archived": false,
"budget": 15000.00,
"user_id": 456,
"customer_id": 410,
"project_type": "fixedBudget",
"kanban_status": null,
"start_date": null,
"end_date": null,
"formatted_start_date": null,
"formatted_end_date": null,
"hourly_rate": 150.00,
"entries_count": 0,
"invoices_count": 0,
"proposals_count": 0,
"tasks_count": 0,
"times_count": 0,
"budget_usage": {
"no_budget": false,
"billable_time_total": "00:00",
"non_billable_time_total": "00:00",
"billable_sum": "0.00",
"total_time": "00:00",
"hourly_rate": "150.00",
"total_sum_invoiced": "0.00",
"total_sum_open": "0.00",
"used_percentage": 0,
"used_chf": "0.00",
"total_budget": "15,000.00",
"remaining": "15,000.00",
"remaining_percentage": 100,
"used_string": "0.00 CHF / 15,000.00 CHF (0%)",
"billability": 100
},
"open_times": {
"readable": "00:00",
"sortable": 0
}
}
}Beispiele
# Projekt mit bestehendem Customer erstellen
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"name": "Website Relaunch",
"customer_id": 410,
"project_type": "fixedBudget",
"budget": 15000.00,
"hourly_rate": 150.00
}' \
"https://app.milkee.ch/api/v2/companies/123/projects"
# Projekt mit neuem Customer erstellen
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"name": "Neue Website",
"newCustomerName": "Neue Firma GmbH",
"project_type": "byHour",
"hourly_rate": 120.00
}' \
"https://app.milkee.ch/api/v2/companies/123/projects"
# Festpreis-Projekt
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"name": "Logo Design",
"customer_id": 410,
"project_type": "fixedPrice",
"budget": 2500.00
}' \
"https://app.milkee.ch/api/v2/companies/123/projects"Einzelnes Project abrufen
GET /companies/{company}/projects/{project}Ruft die Details eines spezifischen Projects ab.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
company | integer | Firmen-ID |
project | integer | Project-ID |
include | string | Zugehörige Daten laden: customer, invoices, proposals, tasks, openTimes, expenses, times, entries |
Response
{
"data": {
"id": 55,
"name": "Website Relaunch",
"notes": "Komplette Überarbeitung der Firmenwebsite inkl. neuem Design",
"archived": false,
"budget": 15000.00,
"user_id": 456,
"customer_id": 410,
"project_type": "fixedBudget",
"kanban_status": "In Progress",
"start_date": "2024-01-15",
"end_date": "2024-03-31",
"formatted_start_date": "15.01.2024",
"formatted_end_date": "31.03.2024",
"hourly_rate": 150.00,
"entries_count": 12,
"invoices_count": 2,
"proposals_count": 1,
"tasks_count": 8,
"times_count": 45,
"customer": {
"id": 410,
"name": "Muster AG",
"number": 1001
},
"budget_usage": {
"no_budget": false,
"billable_time_total": "45:30",
"non_billable_time_total": "05:15",
"billable_sum": "6,825.00",
"total_time": "50:45",
"hourly_rate": "150.00",
"total_sum_invoiced": "5,000.00",
"total_sum_open": "1,825.00",
"used_percentage": 52.5,
"used_chf": "7,875.00",
"total_budget": "15,000.00",
"remaining": "7,125.00",
"remaining_percentage": 47.5,
"used_string": "7,875.00 CHF / 15,000.00 CHF (52.5%)",
"billability": 86.67
},
"open_times": {
"readable": "15:45",
"sortable": 945
}
}
}Beispiele
# Project abrufen
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects/55"
# Mit allen Relationen
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects/55?include=customer,invoices,proposals,tasks,openTimes"Project aktualisieren
PUT /companies/{company}/projects/{project}Aktualisiert ein bestehendes Project.
Request Body
{
"name": "Website Relaunch (Phase 2)",
"budget": 18000.00,
"start_date": "2024-01-15",
"end_date": "2024-04-30",
"kanban_status": "In Progress",
"archived": false
}Validierung
| Feld | Regeln |
|---|---|
name | sometimes, required, string, min:1, max:255 |
customer_id | sometimes, required, integer, exists:customers,id |
hourly_rate | nullable, numeric, min:0, max:999999.99 |
budget | nullable, numeric, min:0, max:9999999.99 |
project_type | sometimes, einer von: byHour, fixedBudget, fixedPrice |
start_date | nullable, date, date_format:Y-m-d |
end_date | nullable, date, date_format:Y-m-d, after_or_equal:start_date |
archived | sometimes, boolean |
kanban_status | nullable, string, max:255 |
Teilweise Updates
Alle Felder sind optional. Sie können nur die Felder übermitteln, die geändert werden sollen.
Response
{
"data": {
"id": 55,
"name": "Website Relaunch (Phase 2)",
"notes": "Komplette Überarbeitung der Firmenwebsite inkl. neuem Design",
"archived": false,
"budget": 18000.00,
"user_id": 456,
"customer_id": 410,
"project_type": "fixedBudget",
"kanban_status": "In Progress",
"start_date": "2024-01-15",
"end_date": "2024-04-30",
"formatted_start_date": "15.01.2024",
"formatted_end_date": "30.04.2024",
"hourly_rate": 150.00,
"entries_count": 12,
"invoices_count": 2,
"proposals_count": 1,
"tasks_count": 8,
"times_count": 45,
"budget_usage": {...},
"open_times": {...}
}
}Beispiele
# Budget aktualisieren
curl -X PUT \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"budget": 18000.00
}' \
"https://app.milkee.ch/api/v2/companies/123/projects/55"
# Projekt archivieren
curl -X PUT \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"archived": true
}' \
"https://app.milkee.ch/api/v2/companies/123/projects/55"
# Projekt-Zeitraum setzen
curl -X PUT \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"start_date": "2024-01-15",
"end_date": "2024-04-30",
"kanban_status": "In Progress"
}' \
"https://app.milkee.ch/api/v2/companies/123/projects/55"Project löschen
DELETE /companies/{company}/projects/{project}Löscht ein bestehendes Project unwiderruflich.
Löschbedingungen
Ein Project kann nur gelöscht werden, wenn:
- Keine verrechneten Zeiteinträge existieren
Andernfalls wird ein 400-Fehler zurückgegeben mit der Meldung, das Projekt zu archivieren.
Response
HTTP/1.1 204 No ContentBeispiel
curl -X DELETE \
-H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/projects/55"Mehrere Projects archivieren/dearchivieren
POST /companies/{company}/projects/multipleArchiviert oder dearchiviert mehrere Projects gleichzeitig.
Request Body
{
"ids": [55, 56, 57],
"archive": true
}| Feld | Typ | Beschreibung |
|---|---|---|
ids | array|integer | Array von Project-IDs oder einzelne ID |
archive | boolean | true zum Archivieren, false zum Dearchivieren (Standard: true) |
Response
HTTP/1.1 204 No ContentBeispiele
# Mehrere Projects archivieren
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"ids": [55, 56, 57],
"archive": true
}' \
"https://app.milkee.ch/api/v2/companies/123/projects/multiple"
# Mehrere Projects dearchivieren
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"ids": [55, 56, 57],
"archive": false
}' \
"https://app.milkee.ch/api/v2/companies/123/projects/multiple"Mehrere Projects löschen
DELETE /companies/{company}/projects/multipleLöscht mehrere Projects gleichzeitig.
Verrechnete Zeiteinträge
Projects mit verrechneten Zeiteinträgen werden automatisch übersprungen und lösen einen 400-Fehler aus. Archivieren Sie diese Projects stattdessen.
Request Body
{
"ids": [55, 56, 57]
}| Feld | Typ | Beschreibung |
|---|---|---|
ids | array|integer | Array von Project-IDs oder einzelne ID |
Response
HTTP/1.1 204 No ContentBeispiel
curl -X DELETE \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{"ids": [55, 56, 57]}' \
"https://app.milkee.ch/api/v2/companies/123/projects/multiple"Fehlerbehandlung
Häufige Fehler
400 Bad Request - Project hat verrechnete Zeiteinträge
{
"message": "Dieses Projekt hat bereits verrechnete Zeiteinträge. Lösche diese zuerst oder archiviere das Projekt."
}Ursache: Project kann nicht gelöscht werden, da bereits Zeiteinträge verrechnet wurden.
Lösung: Archivieren Sie das Project stattdessen:
curl -X PUT \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{"archived": true}' \
"https://app.milkee.ch/api/v2/companies/123/projects/55"422 Validation Error
{
"message": "The given data was invalid.",
"errors": {
"name": [
"Name für Projekt fehlt."
],
"customer_id": [
"Customer existiert nicht."
],
"end_date": [
"Enddatum muss nach oder gleich dem Startdatum sein."
]
}
}Häufige Validierungsfehler:
name.required: Name ist erforderlichname.max: Name zu lang (maximal 255 Zeichen)customer_id.required_without: Entweder customer_id oder newCustomerName muss angegeben werdencustomer_id.exists: Customer existiert nichtcustomer_id.prohibits: customer_id und newCustomerName können nicht gleichzeitig angegeben werdenhourly_rate.numeric: Stundensatz muss numerisch seinhourly_rate.max: Stundensatz darf maximal 999999.99 seinbudget.max: Budget darf maximal 9999999.99 seinend_date.after_or_equal: Enddatum muss nach oder gleich dem Startdatum seinproject_type: Ungültiger Projekttyp (muss byHour, fixedBudget oder fixedPrice sein)
Best Practices
Projekttypen richtig wählen
Wählen Sie den passenden Projekttyp basierend auf der Abrechnungsart:
- byHour: Für stundenbasierte Abrechnung mit flexiblem Umfang
- fixedBudget: Für Projekte mit festem Budget, bei denen die Zeit getrackt wird
- fixedPrice: Für Festpreisprojekte, unabhängig vom Zeitaufwand
Budget-Überwachung
Nutzen Sie das budget_usage Objekt in der Response für Echtzeit-Überwachung des Projektbudgets. Es enthält detaillierte Informationen über verbrauchtes Budget, verrechenbare vs. nicht-verrechenbare Zeit, und Verrechenbarkeitsgrad.
Archivierung vs. Löschung
Archivieren Sie abgeschlossene Projekte statt sie zu löschen. Dies erhält die Historie und verhindert Fehler beim Löschen von Projekten mit verrechneten Zeiteinträgen. Verwenden Sie filter[archived]=false um nur aktive Projekte anzuzeigen.
Customer-Zuordnung
Beim Erstellen eines Projekts können Sie entweder einen bestehenden Customer (customer_id) verwenden oder einen neuen Customer (newCustomerName) erstellen lassen. Beide Felder sind gegenseitig ausschließend.
Kanban-Status
Nutzen Sie das kanban_status Feld für flexible Projekt-Workflows (z.B. "To Do", "In Progress", "Review", "Done"). Der Wert ist frei wählbar und kann an Ihre Prozesse angepasst werden.
Zeitraum-Planung
Setzen Sie start_date und end_date für bessere Projektplanung. Das end_date muss immer nach oder gleich dem start_date sein.
Nächste Schritte
- Customers API - Customers für Projekte verwalten
- Time Tracking API - Zeiterfassung für Projekte
- Entries API - Ausgaben zu Projekten buchen
- Invoices API - Rechnungen für Projekte erstellen
- Code-Beispiele ansehen
- Webhooks einrichten
