Customers API
Customers ermöglichen es Ihnen, Ihre Geschäftskunden zu verwalten. Jeder Customer hat Kontaktinformationen, Adressdetails, Standard-Stundensätze und kann mit Projekten, Rechnungen und Zeiterfassung verknüpft werden.
Customer-Objekt
{
"id": 410,
"company_id": 123,
"user_id": 456,
"name": "Muster AG",
"number": 1001,
"care_of": null,
"po_box": null,
"department": null,
"street": "Beispielstrasse 15",
"zip": "8001",
"city": "Zürich",
"country": "Schweiz",
"website": null,
"phone": null,
"email": "max@example.com",
"default_hourly_rate": 150.00,
"invoice_address": null,
"archived": false,
"tax_rate_id": null,
"created_at": "2023-06-04T15:23:12.000000Z",
"updated_at": "2025-07-17T10:54:45.000000Z"
}Attribute
| Feld | Typ | Beschreibung |
|---|---|---|
id | integer | Eindeutige Customer-ID |
company_id | integer | ID der zugehörigen Firma |
user_id | integer | ID des Benutzers, der den Customer erstellt hat |
name | string | Name des Kundenunternehmens (erforderlich) |
number | integer|null | Kundennummer (automatisch generiert wenn nicht angegeben) |
care_of | string|null | c/o Zusatz für Adresse |
po_box | string|null | Postfach |
department | string|null | Abteilung |
street | string|null | Strassenadresse |
zip | string|null | Postleitzahl |
city | string|null | Stadt |
country | string|null | Land |
website | string|null | Website-URL |
phone | string|null | Telefonnummer |
email | string|null | E-Mail-Adresse |
default_hourly_rate | decimal|null | Standard-Stundensatz (CHF) |
invoice_address | string|null | Separate Rechnungsadresse |
archived | boolean | Ob der Customer archiviert ist |
tax_rate_id | integer|null | ID des zugehörigen Steuersatzes |
created_at | datetime | Erstellungsdatum |
updated_at | datetime | Datum der letzten Aktualisierung |
Endpoints
Alle Customers auflisten
GET /companies/{company}/customersRuft eine paginierte Liste aller Customers der Firma ab.
Parameter
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
page | integer | 1 | Seitennummer |
per_page | integer | 15 | Anzahl Customers pro Seite (max. 100) |
filter[id] | integer | - | Filtern nach spezifischer Customer-ID (exakte Übereinstimmung) |
filter[name] | string | - | Filtern nach Kundenname (Teilübereinstimmung) |
filter[archived] | boolean | - | Filtern nach Archivierungsstatus (exakte Übereinstimmung) |
filter[expenses] | boolean | - | Filtern nach offenen Ausgaben |
filter[times] | integer | - | Filtern nach offenen Zeiten (Minuten) |
include | string | - | Zugehörige Daten laden: contacts, taxRate (kommagetrennt) |
Response
Erweiterte Listen-Response
Die Listen-Response enthält zusätzliche berechnete Felder (open_expenses_sum, open_times_sum, open_invoice_sum).
{
"data": [
{
"id": 410,
"company_id": 123,
"user_id": 456,
"name": "Muster AG",
"number": 1001,
"care_of": null,
"po_box": null,
"department": null,
"street": "Beispielstrasse 15",
"zip": "8001",
"city": "Zürich",
"country": "Schweiz",
"website": null,
"phone": null,
"email": "max@example.com",
"default_hourly_rate": 150.00,
"invoice_address": null,
"archived": false,
"tax_rate_id": 99,
"created_at": "2023-06-04T15:23:12.000000Z",
"updated_at": "2025-07-17T10:54:45.000000Z",
"contacts": [],
"tax_rate": {
"id": 99,
"company_id": 123,
"account_id": 88428,
"field_number": null,
"name": "UST",
"name_en": null,
"name_fr": null,
"name_it": null,
"name_on_documents": "UST",
"rate": 8.1,
"saldo_rate": null,
"calculation_method": "net",
"is_acquisition_tax": false,
"acquisition_account_id": null,
"archived": false,
"created_at": "2025-10-08T06:26:04.000000Z",
"updated_at": "2025-10-08T06:26:04.000000Z"
},
"proposals": [],
"invoices": [],
"active_projects": [],
"open_expenses_sum": {
"sum": 245.75,
"formatted": "245.75"
},
"open_times_sum": {
"hours": 3,
"minutes": 45,
"totalMinutes": 225,
"readable": "03:45"
},
"open_invoice_sum": "1,250.00"
}
],
"links": {
"first": "https://app.milkee.ch/api/v2/companies/123/customers?page=1",
"last": "https://app.milkee.ch/api/v2/companies/123/customers?page=3",
"prev": null,
"next": "https://app.milkee.ch/api/v2/companies/123/customers?page=2"
},
"meta": {
"current_page": 1,
"last_page": 3,
"per_page": 15,
"total": 37
}
}Beispiel
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/customers"Neuen Customer erstellen
POST /companies/{company}/customersErstellt einen neuen Customer für die angegebene Firma.
Request Body
{
"name": "Neue Firma AG",
"contact_name": "Anna Beispiel",
"street": "Beispielstrasse 456",
"zip": "8002",
"city": "Zürich",
"country": "Schweiz",
"email": "contact@example.com",
"phone": "+41 44 987 65 43",
"website": "https://www.example.com",
"default_hourly_rate": 175.00,
"tax_rate_id": 5
}Validierung
| Feld | Regeln |
|---|---|
name | required, string, max:255 |
contact_name | nullable, string, max:255 |
care_of | nullable, string, max:255 |
department | nullable, string, max:255 |
street | nullable, string, max:255 |
zip | nullable, string, max:255 |
city | nullable, string, max:255 |
country | nullable, string, max:255 |
website | nullable, string, max:255, url |
phone | nullable, string, max:255 |
email | nullable, email, max:255 |
po_box | nullable, string, max:255 |
default_hourly_rate | nullable, numeric, min:0, max:999999.99 |
archived | nullable, boolean |
invoice_address | nullable, string |
number | nullable, integer |
tax_rate_id | nullable, integer |
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": 412,
"company_id": 123,
"user_id": 456,
"name": "Neue Firma AG",
"number": 1002,
"care_of": null,
"po_box": null,
"department": null,
"street": "Beispielstrasse 456",
"zip": "8002",
"city": "Zürich",
"country": "Schweiz",
"website": "https://www.example.com",
"phone": "+41 44 987 65 43",
"email": "contact@example.com",
"default_hourly_rate": 175.00,
"invoice_address": null,
"archived": false,
"tax_rate_id": 5,
"created_at": "2024-01-15T14:22:00.000000Z",
"updated_at": "2024-01-15T14:22:00.000000Z",
"contacts": [],
"tax_rate": null,
"proposals": [],
"invoices": [],
"active_projects": [],
"open_expenses_sum": {
"sum": 0,
"formatted": "0.00"
},
"open_times_sum": {
"hours": 0,
"minutes": 0,
"totalMinutes": 0,
"readable": "00:00"
},
"open_invoice_sum": "0.00"
}
}Besonderheiten
- Automatische Kundennummer: Wird generiert wenn nicht angegeben
- Hauptkontakt: Bei
contact_namewird automatisch ein Hauptkontakt erstellt
Beispiel
curl -X POST \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"name": "Neue Firma AG",
"contact_name": "Anna Beispiel",
"email": "contact@example.com",
"default_hourly_rate": 175.00
}' \
"https://app.milkee.ch/api/v2/companies/123/customers"Einzelnen Customer abrufen
GET /companies/{company}/customers/{customer}Ruft die Details eines spezifischen Customers ab, inklusive aller zugehörigen Kontakte.
Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
company | integer | Firmen-ID |
customer | integer | Customer-ID |
include | string | Zugehörige Daten laden: taxRate, contacts, proposals, invoices, activeProjects (kommagetrennt) |
Response
Vollständige Customer-Details
Diese Response enthält vollständige Customer-Details. Um zusätzliche Daten wie Kontakte, Steuersätze, Angebote, Rechnungen oder aktive Projekte zu laden, verwenden Sie den include Parameter. Für Statistiken verwenden Sie den Statistics-Endpoint.
{
"data": {
"id": 410,
"company_id": 123,
"user_id": 456,
"name": "Muster AG",
"number": 1001,
"care_of": null,
"po_box": null,
"department": null,
"street": "Beispielstrasse 15",
"zip": "8001",
"city": "Zürich",
"country": "Schweiz",
"website": null,
"phone": null,
"email": "max@example.com",
"default_hourly_rate": 150.00,
"invoice_address": null,
"archived": false,
"tax_rate_id": null,
"created_at": "2023-06-04T15:23:12.000000Z",
"updated_at": "2025-07-17T10:54:45.000000Z",
"contacts": [],
"tax_rate": null,
"proposals": [],
"invoices": [],
"active_projects": [],
"open_expenses_sum": {
"sum": 245.75,
"formatted": "245.75"
},
"open_times_sum": {
"hours": 3,
"minutes": 45,
"totalMinutes": 225,
"readable": "03:45"
},
"open_invoice_sum": "1,250.00"
}
}Beispiel
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/customers/410"Customer aktualisieren
PUT /companies/{company}/customers/{customer}Aktualisiert einen bestehenden Customer.
Request Body
{
"name": "Muster AG (aktualisiert)",
"default_hourly_rate": 165.00,
"email": "new-email@example.com",
"tax_rate_id": 3
}Response
{
"data": {
"id": 410,
"company_id": 123,
"user_id": 456,
"name": "Muster AG (aktualisiert)",
"number": 1001,
"care_of": null,
"po_box": null,
"department": null,
"street": "Beispielstrasse 15",
"zip": "8001",
"city": "Zürich",
"country": "Schweiz",
"website": null,
"phone": null,
"email": "new-email@example.com",
"default_hourly_rate": 165.00,
"invoice_address": null,
"archived": false,
"tax_rate_id": 3,
"created_at": "2023-06-04T15:23:12.000000Z",
"updated_at": "2024-01-15T15:45:00.000000Z",
"contacts": [],
"tax_rate": null,
"proposals": [],
"invoices": [],
"active_projects": [],
"open_expenses_sum": {
"sum": 245.75,
"formatted": "245.75"
},
"open_times_sum": {
"hours": 3,
"minutes": 45,
"totalMinutes": 225,
"readable": "03:45"
},
"open_invoice_sum": "1,250.00"
}
}Beispiel
curl -X PUT \
-H "Authorization: Bearer 1|abcdef123456789..." \
-H "Content-Type: application/json" \
-d '{
"name": "Muster AG (aktualisiert)",
"default_hourly_rate": 165.00
}' \
"https://app.milkee.ch/api/v2/companies/123/customers/410"Customer löschen
DELETE /companies/{company}/customers/{customer}Löscht einen bestehenden Customer unwiderruflich.
Löschbedingungen
Ein Customer kann nur gelöscht werden, wenn:
- Keine Projekte mit dem Customer verknüpft sind
- Keine Rechnungen für den Customer existieren
Andernfalls wird ein 400-Fehler zurückgegeben.
Response
HTTP/1.1 204 No ContentBeispiel
curl -X DELETE \
-H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/customers/410"Kundenstatistiken abrufen
GET /companies/{company}/customers/{customer}/statisticsRuft detaillierte Statistiken für einen spezifischen Customer ab, inklusive Umsatz, Ausgaben, Gewinn, Stunden und Verrechenbarkeit.
Response
{
"income": "15'000.00",
"expenses": "2'500.00",
"profit": "12'500.00",
"hoursTotal": "45:30",
"hoursBillable": "38:15",
"hoursNonBillable": "07:15",
"hoursOpen": "04:00",
"billability": 84
}| Feld | Typ | Beschreibung |
|---|---|---|
income | string | Gesamtumsatz (formatiert mit Tausendertrennzeichen) |
expenses | string | Gesamtausgaben (formatiert mit Tausendertrennzeichen) |
profit | string | Gewinn (Umsatz - Ausgaben, formatiert) |
hoursTotal | string | Gesamtstunden im Format "HH:MM" |
hoursBillable | string | Verrechenbare Stunden im Format "HH:MM" |
hoursNonBillable | string | Nicht verrechenbare Stunden im Format "HH:MM" |
hoursOpen | string | Offene (noch nicht verrechnete) Stunden im Format "HH:MM" |
billability | integer | Verrechenbarkeit in Prozent (0-100) |
Beispiel
curl -H "Authorization: Bearer 1|abcdef123456789..." \
"https://app.milkee.ch/api/v2/companies/123/customers/410/statistics"Fehlerbehandlung
Häufige Fehler
400 Bad Request - Customer kann nicht gelöscht werden
{
"message": "Kund:in kann nicht gelöscht werden, da noch Projekte vorhanden sind. Lösche diese zuerst oder archiviere den Kunden."
}422 Validation Error
{
"message": "The given data was invalid.",
"errors": {
"name": [
"Name für Kund:in fehlt."
],
"email": [
"E-Mail-Adresse ist nicht gültig."
],
"default_hourly_rate": [
"Stundensatz muss eine Zahl sein."
]
}
}Häufige Validierungsfehler:
name.required: Firmenname ist erforderlichemail.email: Ungültige E-Mail-Adressewebsite.url: Ungültige Website-URLdefault_hourly_rate.numeric: Stundensatz muss numerisch seindefault_hourly_rate.min: Stundensatz muss mindestens 0 seindefault_hourly_rate.max: Stundensatz darf maximal 999999.99 sein
Filterung und Suche
Nach Name
# Customers nach Name suchen (Teilübereinstimmung)
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[name]=Muster"
# Beispiel: Findet "Muster AG", "Musterfirma GmbH", etc.Nach Archivierungsstatus
# Nur aktive Customers
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[archived]=false"
# Nur archivierte Customers
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[archived]=true"Nach offenen Ausgaben
# Customers mit offenen Ausgaben
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[expenses]=true"
# Customers ohne offene Ausgaben
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[expenses]=false"Nach offenen Zeiten
# Customers mit offenen Zeiten (beliebige Anzahl Minuten)
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[times]=1"
# Customers mit mehr als 240 Minuten offene Zeit
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[times]=240"
# Customers ohne offene Zeiten
curl "https://app.milkee.ch/api/v2/companies/123/customers?filter[times]=0"Best Practices
Customer-Daten-Validierung
Für die Erstellung von Dokumenten (z.B. Rechnungen) sollten Sie sicherstellen, dass folgende Mindestdaten vorhanden sind: Name, Strasse, PLZ und Stadt.
Archivierung vs. Löschung
Verwenden Sie Archivierung statt Löschung für bessere Datenintegrität. Ein Customer kann nur gelöscht werden, wenn keine Projekte oder Rechnungen damit verknüpft sind. In den meisten Fällen ist die Archivierung die bessere Wahl.
Effiziente Customer-Synchronisation
Bei der Synchronisation von Customers zwischen Systemen verwenden Sie die Kundennummer (number) als eindeutigen Identifier und vergleichen Sie updated_at Timestamps, um nur geänderte Datensätze zu aktualisieren.
Nächste Schritte
- Customer Contacts API - Kontakte für Customers verwalten
- Projects API - Customer-Projekte verwalten
- Invoices API - Customer-Rechnungen erstellen
- Code-Beispiele ansehen
- Webhooks einrichten
